从零开始搭建一个vue-ssr
前言
上次我们已经实现了从零开始,搭建一个简单的vue-ssr的demo:从零开始搭建一个vue-ssr(上)。那么这次呢,我们基于vue-cli,进行webpack的改造,实现vue-cli版本的vue-ssr。
开始改建
补充安装依赖
与上一次不同,这次我们基于vue-cli进行改建,已经有了很多依赖库了,但我们任需要补充一个核心:
npm install vue-server-renderer
修改客户端的webpack配置
修改webpack.dev.conf.js文件,添加插件
const vueSSRClientPlugin = require("vue-server-renderer/client-plugin");const devWebpackConfig = merge(baseWebpackConfig,{ plugins:[ new vueSSRClientPlugin() ] });
添加了这个配置以后,重新启动项目通过地址就可以访问到vue-ssr-client-manifest.json(http://localhost:8082/vue-ssr-client-manifest.json),页面中出现的内容就是所需要的client-bundle。
修改vue的相关文件
此步骤跟从零开始搭建一个vue-ssr(上)大有相似,不重复描述,直接上代码:
修改 router/index.js
import vueRouter from "vue-router";import Vue from "vue";import HelloWorld from "@/components/HelloWorld";import About from "@/components/About";Vue.use(vueRouter);export default () => { return new vueRouter({ mode:"history", routes:[ { path:"/", component:HelloWorld, name:"HelloWorld" }, { path:"/about", component:About, name:"About" } ] })}
修改app.js
import Vue from "vue";import createRouter from "./router";import App from "./App.vue";export default (context) => { const router = createRouter(); const app = new Vue({ router, components: { App }, template: '<App/>' }); return { app, router }}
修改entry-server.js
import createApp from "./app.js";export default (context) => { return new Promise((reslove,reject) => { let {url} = context; let {app,router} = createApp(context); router.push(url); router.onReady(() => { let matchedComponents = router.getMatchedComponents(); if(!matchedComponents.length){ return reject(); } reslove(app); },reject) })}
修改entry-client.js
import createApp from "./app.js";let {app,router} = createApp();router.onReady(() => { app.$mount("#app");});
修改webpack客户端入口以及输出
修改webpack.base.conf.js
module.exports = { entry:{ app:"./src/entry-client.js" }, output:{ publicPath:"http://localhost:8080/" }};
创建服务端webpakc配置文件
创建build/webpack.server.conf.js文件,目的将插件vue-server-renderer/server-plugin引入server端执行。
const webpack = require("webpack");const merge = require("webpack-merge");const base = require("./webpack.base.conf");// 手动安装// 在服务端渲染中,所需要的文件都是使用require引入,不需要把node_modules文件打包const webapckNodeExternals = require("webpack-node-externals");const vueSSRServerPlugin = require("vue-server-renderer/server-plugin");module.exports = merge(base,{ // 告知webpack,需要在node端运行 target:"node", entry:"./src/entry-server.js", devtool:"source-map", output:{ filename:'server-buldle.js', libraryTarget: "commonjs2" }, externals:[ webapckNodeExternals() ], plugins:[ new webpack.DefinePlugin({ 'process.env.NODE_ENV':'"development"', 'process.ent.VUE_ENV': '"server"' }), new vueSSRServerPlugin() ]});
创建build/dev-server.js,拿到客户端和服务端的bundle文件以及读取到index.html中的模板用作渲染。
const serverConf = require("./webpack.server.conf");const webpack = require("webpack");const fs = require("fs");const path = require("path");// 读取内存中的.json文件// 这个模块需要手动安装const Mfs = require("memory-fs");const axios = require("axios");module.exports = (cb) => { const webpackComplier = webpack(serverConf); var mfs = new Mfs(); webpackComplier.outputFileSystem = mfs; webpackComplier.watch({},async (error,stats) => { if(error) return console.log(error); stats = stats.toJson(); stats.errors.forEach(error => console.log(error)); stats.warnings.forEach(warning => console.log(warning)); // 获取server bundle的json文件 let serverBundlePath = path.join(serverConf.output.path,'vue-ssr-server-bundle.json'); let serverBundle = JSON.parse(mfs.readFileSync(serverBundlePath,"utf-8")); // 获取client bundle的json文件 let clientBundle = await axios.get("http://localhost:8082/vue-ssr-client-manifest.json"); // 获取模板 let template = fs.readFileSync(path.join(__dirname,"..","index.html"),"utf-8"); cb && cb(serverBundle,clientBundle,template); })};
在根目录下创建server.js文件,用于启动服务,并利用createBundleRenderer将两个Bundle和html模板渲染出来。
const devServer = require("./build/dev-server.js");const express = require("express");const app = express();const vueRender = require("vue-server-renderer");app.get('*',(request,respones) => { respones.status(200); respones.setHeader("Content-Type","text/html;charset-utf-8;"); devServer((serverBundle,clientBundle,template) => { let render = vueRender.createBundleRenderer(serverBundle,{ template, clientManifest:clientBundle.data, // 每次创建一个独立的上下文 renInNewContext:false }); render.renderToString({ url:request.url }).then((html) => { respones.end(html); }).catch(error => { respones.end(JSON.stringify(error)); }); });})app.listen(5001,() => { console.log("服务已开启")});
补充一个html模板
根目录下创建index.html
<!DOCTYPE html><html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>vue_cli_ssr</title> </head> <body> <div id="app"> <!--vue-ssr-outlet--> </div> <!-- built files will be auto injected --> </body></html>
最后在package.json中添加一个启动服务端的命令:
"server": "node server.js"
大功告成,开启两个终端,分别输入
npm run devnpm run server
浏览器打开localhost:8082,我们便可以看到由vue-cli改造而成的vue-ssr。
总结
本文是直接基于vue-cli进行改造的一个ssr版本,若想从零开始手撕vue-ssr,可以看上一篇文章从零开始搭建一个vue-ssr(上)。
项目源码
https://github.com/TheWalking...