乐趣区

vueSSR: 从0到1构建vueSSR项目 — node以及vue-cli3的配置

前言
上一次做了路由的相关配置,原本计划今天要做 vuex 部分,但是想了想,发现 vuex 单独的客户端部分穿插解释起来很麻烦,所以今天改做服务端部分。服务端部分做完,再去做 vuex 的部分,这样就会很清晰。
vue ssr 是分两个端,一个是客户端,一个是服务端。所以要做两个 cli3 的配置。那么下面就直接开始做吧。
修改 package.json 的命令
//package.json :client 代表客户端 :server 代表服务端
// 使用 VUE_NODE 来作为运行环境是 node 的标识
//cli3 内置 命令 –no-clean 不会清除 dist 文件
“scripts”: {
“serve:client”: ” vue-cli-service serve”,
“build”:”npm run build:server — –silent && npm run build:client — –no-clean –silent”,
“build:client”: “vue-cli-service build”,
“build:server”: “cross-env VUE_NODE=node vue-cli-service build”,
“start:server”: “cross-env NODE_ENV=production nodemon nodeScript/index”
}

修改 vue.config.js 配置
添加完相关脚本命令之后,我们开始改造 cli3 配置。首先要 require(‘vue-server-renderer’) 然后再根据 VUE_NODE 环境变量来决定编译的走向以及生成不同的环境清单
先做 cli3 服务端的入口文件
// src/entry/server.js
import {
createApp
} from ‘../main.js’

export default context => {

return new Promise((resolve, reject) => {
const {
app,
router
} = createApp(context.data)
// 根据 node 传过来的路由 来调用 router 路由的指向
router.push(context.url)

router.onReady(() => {
// 获取当前路由匹配的组件数组。
const matchedComponents = router.getMatchedComponents()
// 长度为 0 就是没找到该路由所匹配的组件
// 可以路由设置重定向或者传回 node node 来操作也可以
if (!matchedComponents.length) {

return reject({
code: 404
})
}

resolve(app)

}, reject)
})
}

这里是 cli3 的配置

//vue.config.js

const ServerPlugin = require(‘vue-server-renderer/server-plugin’),// 生成服务端清单
ClientPlugin = require(‘vue-server-renderer/client-plugin’),// 生成客户端清单
nodeExternals = require(‘webpack-node-externals’),// 忽略 node_modules 文件夹中的所有模块
VUE_NODE = process.env.VUE_NODE === ‘node’,
entry = VUE_NODE ? ‘server’ : ‘client’;// 根据环境变量来指向入口

module.exports = {
css: {
extract: false// 关闭提取 css, 不关闭 node 渲染会报错
},
configureWebpack: () => ({
entry: `./src/entry/${entry}`,
output: {
filename: ‘js/[name].js’,
chunkFilename: ‘js/[name].js’,
libraryTarget: VUE_NODE ? ‘commonjs2’ : undefined
},
target: VUE_NODE ? ‘node’ : ‘web’,
externals: VUE_NODE ? nodeExternals({
// 设置白名单
whitelist: /\.css$/
}) : undefined,
plugins: [// 根据环境来生成不同的清单。
VUE_NODE ? new ServerPlugin() : new ClientPlugin()
]
}),
chainWebpack: config => {
config.resolve
.alias
.set(‘vue$’, ‘vue/dist/vue.esm.js’)
config.module
.rule(‘vue’)
.use(‘vue-loader’)
.tap(options => {
options.optimizeSSR = false;
return options;
});
config.module
.rule(‘images’)
.use(‘url-loader’)
.tap(options => {
options = {
limit: 1024,
fallback:’file-loader?name=img/[path][name].[ext]’
}
return options;
});
}
}

node 相关配置
用于 node 渲染 必然要拦截 get 请求的。然后根据 get 请求地址来进行要渲染的页面。
官方提供了 vue-server-renderer 插件大概的方式就是 node 拦截所有的 get 请求,然后将获取到的路由地址,传给前台,然后使用 router 实例进行 push
再往下面看之前 先看一下官方文档
创建 BundleRenderercreateBundleRenderer 将 Vue 实例渲染为字符串。renderToString 渲染应用程序的模板 template 生成所需要的客户端或服务端清单 clientManifest
先创建 服务端所需要的模板
//public/index.nodeTempalte.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv=”X-UA-Compatible” content=”IE=edge”>
<meta name=”viewport” content=”width=device-width,initial-scale=1.0″>
<link rel=”icon” href=”/favicon.ico”>
<meta charset=”utf-8″>
<title>vuessr</title>
</head>
<body>
<!–vue-ssr-outlet–>
</body>
</html>

node 部分
先创建三个文件 index.js // 入口 proxy.js // 代理 server.js // 主要配置
//server.js
const fs = require(‘fs’);
const {resolve} = require(‘path’);
const express = require(‘express’);
const app = express();
const proxy = require(‘./proxy’);
const {createBundleRenderer} = require(‘vue-server-renderer’)

// 模板地址
const templatePath = resolve(__dirname, ‘../public/index.nodeTempalte.html’)
// 客户端渲染清单
const clientManifest = require(‘../dist/vue-ssr-client-manifest.json’)
// 服务端渲染清单
const bundle = require(‘../dist/vue-ssr-server-bundle.json’)
// 读取模板
const template = fs.readFileSync(templatePath, ‘utf-8’)
const renderer = createBundleRenderer(bundle,{
template,
clientManifest,
runInNewContext: false
})

// 代理相关
proxy(app);
// 请求静态资源相关配置
app.use(‘/js’, express.static(resolve(__dirname, ‘../dist/js’)))
app.use(‘/css’, express.static(resolve(__dirname, ‘../dist/css’)))
app.use(‘/font’, express.static(resolve(__dirname, ‘../dist/font’)))
app.use(‘/img’, express.static(resolve(__dirname, ‘../dist/img’)))
app.use(‘*.ico’, express.static(resolve(__dirname, ‘../dist’)))

// 路由请求
app.get(‘*’, (req, res) => {
res.setHeader(“Content-Type”, “text/html”)
// 传入路由 src/entry/server.js 会接收到 使用 vueRouter 实例进行 push
const context = {
url: req.url
}

renderer.renderToString(context, (err, html) => {
if (err) {
if (err.url) {
res.redirect(err.url)
} else {
res.status(500).end(‘500 | 服务器错误 ’);
console.error(`${req.url}: 渲染错误 `);
console.error(err.stack)
}
}
res.status(context.HTTPStatus || 200)
res.send(html)
})
})
module.exports = app;

//proxy.js

const proxy = require(‘http-proxy-middleware’);

function proxyConfig(obj){
return {
target:’localhost:8081′,
changeOrigin:true,
…obj
}
}
module.exports = (app) => {
// 代理开发环境
if (process.env.NODE_ENV !== ‘production’) {
app.use(‘/js/main*’, proxy(proxyConfig()));
app.use(‘/*hot-update*’,proxy(proxyConfig()));
app.use(‘/sockjs-node’,proxy(proxyConfig({ws:true})));
}
}

//index.js
const app = require(‘./server’)

app.listen(8080, () => {
console.log(‘\033[42;37m DONE \033[40;33m localhost:8080 服务已启动 \033[0m’)
})

做完这一步之后,就可以预览基本的服务渲染了。后面就只差开发环境的配置,以及到 node 数据的传递 (vuex)
npm run build
npm run start:server
打开 localhost:8080
F12 – Network – Doc 就可以看到内容

最终目录结构
|– vuessr
|– .gitignore
|– babel.config.js
|– package-lock.json
|– package.json
|– README.md
|– vue.config.js
|– nodeScript //node 渲染配置
| |– index.js
| |– proxy.js
| |– server.js
|– public// 模板文件
| |– favicon.ico
| |– index.html
| |– index.nodeTempalte.html
|– src
|– App.vue
|– main.js
|– router.config.js// 路由集合
|– store.config.js//vuex 集合
|– assets// 全局静态资源源码
| |– 备注.txt
| |– img
| |– logo.png
|– components// 全局组件
| |– Head
| |– index.js
| |– index.scss
| |– index.vue
| |– img
| |– logo.png
|– entry//cli3 入口
| |– client.js
| |– server.js
| |– 备注.txt
|– methods// 公共方法
| |– 备注.txt
| |– mixin
| |– index.js
|– pages// 源码目录
| |– home
| | |– index.js
| | |– index.scss
| | |– index.vue
| | |– img
| | | |– flow.png
| | | |– head_portrait.jpg
| | | |– logo.png
| | | |– vuessr.png
| | |– vue
| | | |– index.js
| | | |– index.scss
| | | |– index.vue
| | |– vueCli3
| | | |– index.js
| | | |– index.scss
| | | |– index.vue
| | |– vueSSR
| | | |– index.js
| | | |– index.scss
| | | |– index.vue
| | |– vuex
| | |– index.js
| | |– index.scss
| | |– index.vue
| |– router// 路由配置
| | |– index.js
| |– store//vuex 配置
| |– all.js
| |– gather.js
| |– index.js
|– static//cdn 资源
|– 备注.txt

github 欢迎 watch

退出移动版