一、服务端渲染相关的概念
什么是渲染?
例如对于我们前端开发者来说最常见的一种场景就是:请求后端接口数据,然后将数据通过模板绑定语法绑定到页面中,最终呈现给用户。
数据:
模板:
渲染(数据+模板)结果:
把(数据+模板)拼接到一起的这事儿就是我们这里所指的渲染。
二、传统的Web应用
最早期,Web页面渲染都是在服务端完成的,即服务端运行过程中将所需的数据结合页面模板渲染为HTML,响应给客户端浏览器。所以浏览器呈现出来的是直接包含内容的页面。
工作流程:
这种方式的代表性技术有:ASP、PHP、JSP等,再到后来的一些相对高级一点的服务端框架配合一些模板引擎。
无论如何这种方式对于没有玩儿过后端开发的来说可能会比较陌生,所以下面通过我们前端比较熟悉的Node.js来了解一下这种方式。
这也就是最早的网页渲染方式,也就是动态网站的核心工作步骤。在这样的一个工作过程中,因为页面中的内容不是固定的,它有一些动态的内容。
const express = require('express')
const fs = require('fs')
const app = express()app.get('/', (req, res) => {// 1. 普通文本// res.send('Hello World')// 2. HTML 格式文本// res.send('<h2>Hello World</h2>')// 3. 为了便于开发和维护,把文件内容放到单独文件// fs.readFile('./views/index.html', (err, data) => {// if(err) {// return res.status(404).send('404 Not Found')// }// res.end(data)// })// 4. 动态页面渲染const todos = [{id: 1,title: '吃饭1'},{id: 2,title: '吃饭2'},{id: 3,title: '吃饭3'},{id: 4,title: '吃饭4'}]fs.readFile('./views/index.html', 'utf8', (err, data) => {if(err) {return res.status(404).send('404 Not Found')}// 获取数据// 获取页面模板// 数据 + 模板 = 完整页面let str = ''todos.forEach(todo => {str += `<li>${todo.title}</li>`})const ret = data.replace('(*^▽^*)', str)res.end(ret)})// 5. 使用模板引擎
})app.listen(3000, () => {console.log(`Server running at http://localhost:3000/`)
})
在今天看来,这种渲染模式是不合理或者说不先进的。因为在当下这种网页越来越复杂的情况下,这种模式存在很多明显的不足:
- 应用的前后端部分完全耦合在一起,在前后端协同开发方面会有非常大的阻力;
- 前端没有足够的发挥空间,无法充分利用现在前端生态下的一些更优秀的方案;
- 由于内容都是在服务端动态生成的,所以服务端的压力较大;
- 相比目前流行的SPA应用来说,用户体验一般;
但是不得不说,在网页应用并不复杂的情况下,这种方式也是可取的。
三、art-template 模板引擎
npm install art-template --save
const express = require('express')
const fs = require('fs')
const path = require('path')
const app = express()
const template = require('art-template')// 当渲染以 .art结尾的资源文件的时候 使用express-art-template
app.engine('art', require('express-art-template'))
// art-template 模板引擎的配置
app.set('view options', {debug: process.env.NODE_ENV !== 'production'
})
// 配置模板文件的存储目录
app.set('views', path.join(__dirname, 'views'))
// 可以省略的模板文件后缀名
app.set('view engine', 'art')app.get('/', (req, res) => {// 1. 普通文本// res.send('Hello World')// 2. HTML 格式文本// res.send('<h2>Hello World</h2>')// 3. 为了便于开发和维护,把文件内容放到单独文件// fs.readFile('./views/index.html', (err, data) => {// if(err) {// return res.status(404).send('404 Not Found')// }// res.end(data)// })// 4. 动态页面渲染// const todos = [// {// id: 1,// title: '吃饭1'// },// {// id: 2,// title: '吃饭2'// },// {// id: 3,// title: '吃饭3'// },// {// id: 4,// title: '吃饭4'// }// ]// fs.readFile('./views/index.html', 'utf8', (err, data) => {// if(err) {// return res.status(404).send('404 Not Found')// }// // 获取数据// // 获取页面模板// // 数据 + 模板 = 完整页面// let str = ''// todos.forEach(todo => {// str += `<li>${todo.title}</li>`// })// const ret = data.replace('(*^▽^*)', str)// res.end(ret)// })// 5. 使用模板引擎// 1.读取模板内容// fs.readFile('./views/index.html', 'utf8', (err, templateStr) => {// if(err) {// return res.status(404).send('404 Not Found')// }// // 2.获取数据const todos = [{id: 1,title: '吃饭1'},{id: 2,title: '吃饭2'},{id: 3,title: '吃饭3'},{id: 4,title: '吃饭4'}]// // 3.渲染这件事是在服务端完成的// const ret = template.render(templateStr, { // 模板中使用的数据// foo: 'bar',// todos// })// res.end(ret)// })// 1.读取模板文件// 2.渲染// 3.发送响应res.render('index.art', {foo: 'bar',todos: todos})})
app.listen(3000, () => {console.log(`Server running at http://localhost:3000/`)
})
四、在Express 中托管静态资源
4.1 配置选项
4.2 托管多个资源目录
注意:
建议加上前缀,防止出现比如
/node_modules里面有css/main.css
/public里面也有css/main.css,
如果这时没有加前缀的话,默认访问的是前面的那个(也就是上面代码中第一行的)目录里面的css/main.css
4.3 页面中的资源路径问题
关于页面中的静态资料路径,建议使用 绝对路径 / , / 代表的就是当前网站的根目录。
如果使用相对路径的话,./相对的并不是文件中的路径,而是请求的url,这样容易出现静态资源访问不到的问题