服务端搭建
在讲解完webpack
相关配置的编写之后,我们就需要搭建我们用于提供渲染能力的服务端代码了。
# Server端
这里我们在上代码之前,我们先需要理一理思路,先回顾一下我们前面实现的一个小型的服务端渲染过程,也就是直接在服务端代码中new
一个Vue
实例,接着调用createBundleRenderer
创建一个renderer
,然后使用renderer.renderToString
方法进行渲染,所以,整体流程大致为:
- 获取实例。
- 创建
renderer
。 - 执行渲染。
- 返回给客户端。
就以上流程而言,我们这里的方式其实差不多,只不过在各步骤中需要做一些额外的操作。
以下服务端代码文件位于
VueSSR-Lesson
项目下lessons
目录
# 获取项目代码
// lesson2.js
let clientManifest = require(path.resolve(__dirname, '../dist', 'vue-ssr-client-manifest.json'));
let serverBundle = require(path.resolve(__dirname, '../dist', 'vue-ssr-server-bundle.json'));
2
3
首先我们根据前面配置的客户端与服务端相关构建的配置,分别打包构建完并待相关结果输出到dist
目录下之后,就可以直接在服务端代码中将客户端配置构建的manifest
文件与服务端配置构建的bundle
文件进行导入,后续会需要配合生成renderer
。
# 创建renderer
const { createBundleRenderer } = require('vue-server-renderer');
const template = fs.readFileSync('./index.html', 'utf-8')
const renderer = createBundleRenderer(serverBundle, {
template,
clientManifest,
runInNewContext: false
});
2
3
4
5
6
7
这里主要用到了vue-server-renderer
的createBundleRenderer
,并读取了一个模板文件template
用于渲染,按照相关配置参数,分别传入serverBundle
和clientManifest
,同时,设置runInNewContext
为false
,这一步主要是让关闭所有请求自动创建一个新的上下文,这种方式可以减少服务器的压力,毕竟对于服务端而言,如果在请求量过多的情况下,这种方式开销会比较大。
# 执行渲染&返回给客户端
app.use(express.static(path.resolve(__dirname, '../dist')));
router.get('*', (req, res) => {
const context = { url: req.url };
renderer.renderToString(context, (err, html) => {
if (err) {
if (err.code === 404) {
return res.end('404 Not Found');
}
console.log(err)
};
res.setHeader('Content-Type', 'text/html')
res.send(html);
})
})
2
3
4
5
6
7
8
9
10
11
12
13
14
这里为了方便就直接将dist
目录开放为静态资源目录了,同时拦截所有请求执行渲染逻辑,将渲染成功后的结果发送给客户端进行展示。
首先我们可以关注一下这个context
对象,内容主要是一个请求的url
,主要用于获取当前用户请求的路由,做一些数据预取的操作,具体使用这个context
的代码可以参见前面的服务端entry-server.js
文件,入参context
就是这个地方的context
对象,这里会自动调用我们的服务端打包出来的入口方法,获取到我们整个业务代码的实例(参见我们在entry-server.js
中resolve
的app
实例),这样我们就能正确渲染出我们想要的页面了。
得到渲染后的结果之后,我们就能直接调用express
为我们提供的send
方法或end
方法给客户端返回结果了。
# 完整代码
const { createBundleRenderer } = require('vue-server-renderer');
const express = require('express');
const app = express();
const router = express.Router();
const chalk = require('chalk');
const fs = require('fs');
const template = fs.readFileSync('./index.html', 'utf-8')
const path = require('path');
let clientManifest = require(path.resolve(__dirname, '../dist', 'vue-ssr-client-manifest.json'));
let serverBundle = require(path.resolve(__dirname, '../dist', 'vue-ssr-server-bundle.json'));
app.use(express.static(path.resolve(__dirname, '../dist')));
const renderer = createBundleRenderer(serverBundle, {
template,
clientManifest,
runInNewContext: false
});
router.get('*', (req, res) => {
const context = { url: req.url };
renderer.renderToString(context, (err, html) => {
if (err) {
if (err.code === 404) {
return res.end('404 Not Found');
}
console.log(err)
};
res.setHeader('Content-Type', 'text/html')
res.send(html);
})
})
app.use(router);
app.listen(3000, function() {
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
31
32
33
34
35
36
37
38
39
40
41
# 预览效果
直接通过node
执行该文件,我们就能通过浏览器访问到我们的应用了。
在启动服务之前,请确保你的客户端manifest文件和服务端构建bundle已经构建完毕了,如果没有请先构建一下,也就是前面提到的webpack配置的两份打包配置文件。