从零开始搭建一个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: ' '
});
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
vue_cli_ssr
最后在package.json中添加一个启动服务端的命令:
"server": "node server.js"
大功告成,开启两个终端,分别输入
npm run dev
npm run server
浏览器打开localhost:8082,我们便可以看到由vue-cli改造而成的vue-ssr。
总结
本文是直接基于vue-cli进行改造的一个ssr版本,若想从零开始手撕vue-ssr,可以看上一篇文章从零开始搭建一个vue-ssr(上)。