什么是服务端渲染
服务端渲染(Server Side Render)简称ssr,是一种直接从服务端返回渲染内容到客户端的页面渲染方式。
服务端渲染的好处
- 首屏渲染速度快
- 有利于SEO
服务端渲染的过程
- 客户端发起请求时,服务端通过读取文件模板以及获取异步数据,结合生成客户端的首屏渲染内容,返回到客户端。
- 客户端收到返回内容后,直接渲染返回结果,然后再激活客户端渲染。以Vue为例,即重新$mount渲染Vue实例,由于Vue实例已经挂载,所以不用重新渲染DOM,只会在客户端生成Vue实例,并将数据变为响应式的数据。
服务端渲染的构建过程
按照服务端渲染的过程,服务端渲染的构建会有两个入口,客户端渲染的入口以及服务端渲染的入口。打包之后也会对应生成客户端和服务端的入口bundle.js,触发渲染时,后执行对应的bundle.js。具体构建过程如下图:
从零开始搭建
服务端渲染的基本结构
-
在一个空的文件夹中初始化package.json,创建src文件目录用于存放源码文件。
-
安装依赖
- 生产依赖:
- vue:vue.js
- vue-server-renderer:vue服务端渲染器
- express:帮助生成web服务器
- cross-env:跨平台设置全局环境
- 开发依赖:
- webpack打包依赖:webpack、webpack-cli、webpack-merge(合并webpack配置)、webpack-node-externals(排除wbepack中的node模块)、friendly-errors-webpack-plugin(打包日志友好输出)。
- babel转换相关:babel-loader、@babel/core、@babel/plugin-transform-runtime、@babel/preset-env。
- vue转换相关:vue-loader、vue-template-compiler
- css及文件转换:css-loader、filer-loader、url-loader。
- 生产依赖:
-
在src中创建Vue实例模板App.vue以及入口模板文件app.js。
/* App.vue */ <template> <div id="app"> </div> </template> <script> export default { } </script> <style> </style>
/* app.js */ /** * 生成Vue实例 */ import Vue from 'vue' import App from './App' export function createApp() { const app = new Vue({ render: h => h(App) }) return { app } }
-
根目录下创建server.js,用于启动web服务,并将服务端渲染的内容发送到客户端。
// server.js // 创建Vue实例 const Vue = require('vue') const app = new Vue({ template: `<div id="app">{ { title }}</div>`, data() { return { title: '吴绍清' } } }) // 创建渲染器 const renderer = require('vue-server-renderer').createRenderer() // 创建服务器对象 const server = require('express')() server.get('/', (req, res) => { res.setHeader('Content-Type', 'text/html;charset=utf-8') // 设置返回内容的编码格式,反之乱码 // 将Vue实例渲染为 HTML 字符串 renderer.renderToString(app, (err, html) => { if (err) return res.status(500).end(err) console.log(html); res.end(html) }) }) server.listen(8000)
-
通过node server.js来启动web服务器,访问
http://localhost:8000/
即可看到服务端渲染的内容。此时是直接返回模板的html字符串内容。
-
为渲染器设置模板。需要创建一个模板文件index.template.html。然后在创建渲染器时读取模板,并传入。并在renderToString时将数据对象作为第二个参数传入。
<!-- index.template.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <!-- 三花括号进行 HTML 不转义差值 --> { { { meta }}} <!-- 双花括号进行 HTML 转义差值 --> <title>{ { title }}</title> </head> <body> <!-- 固定写法,服务度渲染的出口,Vue实例将替换下面的注释标签 --> <!--vue-ssr-outlet--> </body> </html>
//server.js修改 // 创建渲染器 const renderer = require('vue-server-renderer').createRenderer({ template: require('fs').readFileSync('./index.template.html', 'utf-8') }) // 创建服务器对象 const server = require('express')() server.get('/', (req, res) => { res.setHeader('Content-Type', 'text/html;charset=utf-8') // 设置返回内容的编码格式,反之乱码 // 将Vue实例渲染为 HTML 字符串 renderer.renderToString(app, { title: '模板插值', meta: `<meta http-equiv="X-UA-Compatible" content="IE=edge" />` }, (err, html) => { if (err) return res.status(500).end(err) res.end(html) }) })
-
启动服务器之后,访问
http://localhost:8000/
,将返回模板与数据的结合结果
打包构建设置
-
src目录下创建entry-client.js和entry-server.js文件,作为客户端和服务端打包的入口文件。
// entry-client.js /** * 客户端渲染入口 */ import createApp from './app' const { app } = createApp() // 在id为app的div中渲染Vue实例 app.$mount('#app')
// entry-server.js import createApp from './app' export default context => { const { app } = createApp() return app }
-
创建打包配置文件webpack.base.config.js作为公共打包配置,webpack.client.config.js作为客户端打包配置,webpack.server.config.js作为服务端打包配置。
// webpack.b