快速上手
在经过了一轮相关理论的毒打之后,我们就要准备自己动手来给我们的应用披上服务端渲染的雍容新衣了。这里将会逐步带你由浅入深来剖析如何实现一个SSR
,并逐步提高我们构建的项目所能适应的应用场景。
任何只提概念不提实践的
SSR
之类文章都是在耍流氓。
# 阅读建议
node.js
基础- 了解
express
搭建简单服务 - 了解
Vue
。
# 编写一段简单的Vue相关代码
我们这里结合实际代码来研究会比较好理解一些:
const Vue = require('vue');
const app = new Vue({
template: '<div>{{text}}</div>',
data() {
return {
text: '这里是服务端渲染Demo'
}
}
});
2
3
4
5
6
7
8
9
这里是用
node
配合express
搭建了一个简易的node
服务。
这里首先引入了Vue
,并实例化了一个Vue
实例,该实例上挂载了一个简单的模板内容,也就是一个template
,并使用了Vue
的插值表达式给模板中div
中插入了一段文字,这里的内容从data
对象中拿到,相信写过Vue
的小伙伴应该也会十分眼熟,这里就不过多赘述了。
# 启动一个服务
const server = express();
server.listen(3000, () => {
console.log(
'App runing at:',
`Local: ${ chalk.blueBright.underline('http://localhost:3000') }`
)
});
2
3
4
5
6
7
接下来笔者这里利用express
启动了一个服务,并绑定在3000端口,方便我们直接访问服务就能获取到内容。
这里笔者用了
chalk
库对我们控制台打印的结果修饰了一下,去掉也无伤大雅,直接console.log()
就行。
做完这些我们就需要思考下一个点了,服务也写好了,Vue
相关代码也写好了,我们怎么把内容渲染给客户端呢?
# 渲染Vue实例
这个时候就要用到我们官方提供的第三方包vue-server-renderer
,顾名思义,就是专门用来做Vue
服务端渲染的。首先我们先拿到它导出的方法createRenderer
,并调用该方法,获得一个renderer
,接着我们就可以给服务写一个中间件来拦截请求并返回给客户端了。
const { createRenderer } = require('vue-server-renderer');
const renderer = createRenderer();
server.use((req, res) => {
renderer.renderToString(app, (err, html) => {
if (err) throw err;
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.end(html);
})
})
2
3
4
5
6
7
8
9
我们这里调用renderer
上的renderToString
方法,并将我们的Vue
实例作为第一个参数传入,同时编写一个回调,用来接收渲染好的HTML
字符串,这样我们就可以在该回调用拿到渲染好的字符串并发送给客户端了。
这里记得给响应加上头信息,不然客户端接收到的可能就是乱码。
同时,这里的renderToString
也可以换成renderToStream
,采用流式渲染,具体用法这里直接贴出官方给的方式,就不过多介绍了。
const stream = renderer.renderToStream(context)
let html = ''
stream.on('data', data => {
html += data.toString()
})
stream.on('end', () => {
console.log(html) // 渲染完成
})
stream.on('error', err => {
// handle error...
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
在流式渲染模式下,当 renderer
遍历虚拟 DOM
树 (virtual DOM tree
) 时,会尽快发送数据。这意味着我们可以尽快获得"第一个 chunk
",并开始更快地将其发送给客户端。
然而,当第一个数据 chunk
被发出时,子组件甚至可能不被实例化,它们的生命周期钩子也不会被调用。这意味着,如果子组件需要在其生命周期钩子函数中,将数据附加到渲染上下文 (render context
),当流 (stream
) 启动时,这些数据将不可用。这是因为,大量上下文信息 (context information
)(如头信息 (head information
) 或内联关键 CSS(inline critical CSS
))需要在应用程序标记 (markup
) 之前出现,我们基本上必须等待流(stream
)完成后,才能开始使用这些上下文数据。
因此,如果你依赖由组件生命周期钩子函数填充的上下文数据,则不建议使用流式传输模式。
上面官方的解释已经挺详细了,这里笔者再多提一嘴,流式传输可以用在一些静态页面,不依赖生命周期函数进行数据填充的场景,这种场景下,我们可以尽可能的让用户早点看到内容,而不影响原有渲染流程。
# 完整代码
const { createRenderer } = require('vue-server-renderer');
const express = require('express');
const chalk = require('chalk')
const Vue = require('vue');
const app = new Vue({
template: '<div>{{text}}</div>',
data() {
return {
text: '这里是服务端渲染Demo'
}
}
});
const renderer = createRenderer();
const server = express();
server.use((req, res) => {
renderer.renderToString(app, (err, html) => {
if (err) throw err;
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.end(html);
})
})
server.listen(3000, () => {
console.log(
'App runing at:',
`Local: ${ chalk.blueBright.underline('http://localhost:3000') }`
)
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
效果:
这里我们实现了一个简单的服务端渲染示例,启动服务之后我们就能访问对应端口查看渲染结果了。
← 介绍 Vue项目添加SSR→