彻底理解服务端渲染 - SSR原理( 四 )


于是我们得到了客户端的构建配置,vue.client.config.js
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')module.exports = {configureWebpack: () => ({entry: `./src/client-entry.js`,devtool: 'source-map',target: 'web',plugins: [new VueSSRClientPlugin()]}),chainWebpack: config => {// 去除所有关于客户端生成的html配置,因为已经交给后端生成config.plugins.delete('html');config.plugins.delete('preload');config.plugins.delete('prefetch');}};使用vue-server-renderer提供的client-server,主要作用是生成构建加过清单
vue-ssr-client-manifest.json,服务端在渲染页面时,根据这个清单来渲染HTML中的script标签(JavaScript)和link标签(CSS) 。
接下来,我们需要将vue.client.config.js和vue.server.config.js都交给vue-cli内置的构建配置文件vue.config.js,根据环境变量使用不同的配置
// vue.config.jsconst TARGET_NODE = process.env.WEBPACK_TARGET === 'node';const serverConfig = require('./vue.server.config');const clientConfig = require('./vue.client.config');if (TARGET_NODE) {module.exports = serverConfig;} else {module.exports = clientConfig;}使用cross-env区分环境
{"scripts": {"server": "babel-node src/server.js","serve": "vue-cli-service serve","build": "vue-cli-service build","build:server": "cross-env WEBPACK_TARGET=node vue-cli-service build --mode server"}}模板组件共享第一步:创建VUE实例为了实现模板组件共享,我们需要将获取 Vue 渲染实例写成通用代码,如下 createApp:
import Vue from 'vue';import App from './App';export default function createApp (context) {const app = new Vue({render: h => h(App)});return {app};};第二步:客户端实例化VUE新建客户端项目的入口文件,client-entry.js
import Vue from 'vue'import createApp from './createApp';const {app} = createApp();app.$mount('#app');client-entry.js是浏览器渲染的入口文件,在浏览器加载了客户端编译后的代码后,组件会被渲染到id为app的元素节点上 。
第三步:服务端实例化VUE新建服务端代码的入口文件,server-entry.js
import createApp from './createApp'export default context => {const { app } = createApp(context);return app;}server-entry.js是提供给服务器渲染vue组件的入口文件,在浏览器通过URL访问到服务器后,服务器需要使用server-entry.js提供的函数,将组件渲染成html 。
第四步:HTTP服务所有东西的准备好之后,我们需要修改nodejs的HTTP服务器的启动文件 。首先,加载服务端代码server-entry.js的webpack构建结果
const path = require('path');const serverBundle = path.resolve(process.cwd(), 'serverDist', 'vue-ssr-server-bundle.json');const {createBundleRenderer} = require('vue-server-renderer');const serverBundle = path.resolve(process.cwd(), 'serverDist', 'vue-ssr-server-bundle.json');加载客户端代码client-entry.js的webpack构建结果
const clientManifestPath = path.resolve(process.cwd(), 'dist', 'vue-ssr-client-manifest.json');const clientManifest = require(clientManifestPath);使用 vue-server-renderer 的createBundleRenderer创建一个html渲染器:
const template = fs.readFileSync(path.resolve(__dirname, 'index.html'), 'utf-8');const renderer = createBundleRenderer(serverBundle, {template,// 使用HTML模板clientManifest // 将客户端的构建结果清单传入});创建HTML模板,index.html
<html><head><title>SSR</title></head><body><!--vue-ssr-outlet--></body></html>在HTML模板中,通过传入的客户端渲染结果clientManifest,将自动注入所有link样式表标签,而占位符将会被替换成模板组件被渲染后的具体的HTML片段和script脚本标签 。
HTML准备完成后,我们在server中挂起所有路由请求
const express = require('express');const app = express();/* code todo 实例化渲染器renderer */app.get('*', function(req, res) {renderer.renderToString({}, (err, html) => {if (err) {res.send('500 server error');return;}res.send(html);})});接下来,我们构建客户端、服务端项目,然后执行 node server.js,打开页面源代码,

彻底理解服务端渲染 - SSR原理

文章插图
 
看起来是符合预期的,但是发现控制台有报错,加载不到客户端构建css和js,报404,原因很明确,我们没有把客户端的构建结果文件挂载到服务器的静态资源目录,在挂载路由前加入下面代码:


推荐阅读