Vue SSR 指南(二)
根据官网按步骤实现Vue SSR搭建过程
使用 webpack 的源码结构(vue-cli3)
新建一个项目,使用vue-cli3脚手架搭建,vue create ssr-demo-webpack 根据自己需求选择,项目结构如下:
1.main.js
main.js 是我们应用程序的「通用 entry」。在纯客户端应用程序中,我们将在此文件中创建根 Vue 实例,并直接挂载到 DOM。但是,对于服务器端渲染(SSR),责任转移到纯客户端 entry 文件。main.js 简单地使用 export 导出一个 createApp 函数:
import Vue from 'vue'
import App from './App.vue'
// 导出一个工厂函数,用于创建新的
// 应用程序、router 和 store 实例
export function createApp() {
const app = new Vue({
// 根实例简单的渲染应用程序组件。
render: (h) => h(App),
})
return { app }
}
2.entry-client.js
客户端 entry 只需创建应用程序,并且将其挂载到 DOM 中:
import { createApp } from './main'
// 客户端特定引导逻辑……
const { app, router } = createApp()
// 这里假定 App.vue 模板中根元素具有 `id="app"`
router.onReady(() => {
app.$mount('#app')
})
3.entry-server.js
服务器 entry 使用 default export 导出函数,并在每次渲染中重复调用此函数。此时,除了创建和返回应用程序实例之外,它不会做太多事情 - 但是稍后我们将在此执行服务器端路由匹配 (server-side route matching) 和数据预取逻辑 (data pre-fetching logic)。
import { createApp } from './app'
export default context => {
const { app } = createApp()
return app
}
过程简述
entry-server.js构建后成为 vue-ssr-server-bundle.json文件(vue代码) server.js将 vue-ssr-server-bundle.json (vue代码)注入到模版文件中,并输出,暂时未用到 entry-client.js
4.构建配置
npm install cross-env webpack-node-externals vue-server-renderer --save-dev
调整 webpack 配置最简单的方式就是在 vue.config.js 中的 configureWebpack 选项提供一个对象,
该对象将会被 webpack-merge 合并入最终的 webpack 配置。
注意点:不修改以下配置编译报错
1.allowlist: [/.css$/], // [webpack-node-externals] : Option ‘whitelist’ is not supported. Did you mean ‘allowlist’?
2. splitChunks: TARGET_NODE ? false : undefined, //不配置报错 Server-side bundle should have one single entry file. Avoid using CommonsChunkPlugin in the server config. vuessr 报错解决
const nodeExternals = require('webpack-node-externals')
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')
// 环境变量:决定入口是客户端还是服务端
const TARGET_NODE = process.env.WEBPACK_TARGET === 'node'
const target = TARGET_NODE ? 'server' : 'client'
module.exports = {
configureWebpack: () => {
if (process.env.WEBPACK_TARGET === 'node') {
return {
entry: './src/entry-server.js',
target: 'node',
devtool: 'source-map',
output: {
libraryTarget: 'commonjs2',
},
externals: [
nodeExternals({
allowlist: [/\.css$/], // [webpack-node-externals] : Option 'whitelist' is not supported. Did you mean 'allowlist'?
}),
],
optimization: {
splitChunks: TARGET_NODE ? false : undefined, //不配置报错 Server-side bundle should have one single entry file. Avoid using CommonsChunkPlugin in the server config. vuessr 报错解决
},
plugins: [new VueSSRServerPlugin()],
}
} else {
return {
entry: './src/entry-client.js',
plugins: [new VueSSRClientPlugin()],
}
}
},
}
5.修改package.json
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"build:client": "vue-cli-service build",
"build:server": "cross-env WEBPACK_TARGET=node vue-cli-service build --mode server",
"build:win": "npm run build:server && move dist\\vue-ssr-server-bundle.json bundle && npm run build:client && move bundle dist\\vue-ssr-server-bundle.json",
"build:mac": "npm run build:server && mv dist/vue-ssr-server-bundle.json bundle && npm run build:client && mv bundle dist/vue-ssr-server-bundle.json"
},
根据自己系统运行 npm run build:win或者
npm run build:mac`,会在根目录下生成dist文件夹
6.服务端
新建server.js
const serverBundle = require('./dist/vue-ssr-server-bundle.json')
const express = require('express')
const app = express()
const path = require('path')
const renderer = require('vue-server-renderer').createBundleRenderer(serverBundle, {
template: require('fs').readFileSync('./index.template.html', 'utf-8'),
})
app.get('*', (req, res) => {
const context = {
title: 'ssr',
meta: `
<meta charset="utf-8">
`,
url: req.url,
}
renderer.renderToString(context, (err, html) => {
if (err) {
res.status(500).end('Internal Server Error')
return
}
res.end(html)
})
})
app.listen(8080, () => {
console.log('已监听 localhost:8080')
})
新建
index.template.html
<!DOCTYPE html>
<html lang="en">
<head>
<!-- 使用三花括号(triple-mustache)进行 HTML 不转义插值(non-HTML-escaped interpolation) -->
{{{ meta }}}
<!-- 使用双花括号(double-mustache)进行 HTML 转义插值(HTML-escaped interpolation) -->
<title>{{ title }}</title>
</head>
<body>
<!--vue-ssr-outlet-->
</body>
</html>
运行node server.js
,并打开浏览器(因为还未用到router,所以home|about不可跳转路由)
7.源码