上次咱們已經實現了從零開始,搭建一個簡單的vue-ssr的demo:從零開始搭建一個vue-ssr(上)。那麼此次呢,咱們基於vue-cli,進行webpack的改造,實現vue-cli版本的vue-ssr。html
與上一次不一樣,此次咱們基於vue-cli進行改建,已經有了不少依賴庫了,但咱們任須要補充一個核心:vue
npm install vue-server-renderer
修改webpack.dev.conf.js文件,添加插件node
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。webpack
此步驟跟從零開始搭建一個vue-ssr(上)大有類似,不重複描述,直接上代碼:
修改 router/index.jsios
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.jsgit
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.jsgithub
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.jsweb
import createApp from "./app.js"; let {app,router} = createApp(); router.onReady(() => { app.$mount("#app"); });
修改webpack.base.conf.jsvue-router
module.exports = { entry:{ app:"./src/entry-client.js" }, output:{ publicPath:"http://localhost:8080/" } };
建立build/webpack.server.conf.js文件,目的將插件vue-server-renderer/server-plugin引入server端執行。vue-cli
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("服務已開啓") });
根目錄下建立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 dev npm run server
瀏覽器打開localhost:8082,咱們即可以看到由vue-cli改造而成的vue-ssr。
本文是直接基於vue-cli進行改造的一個ssr版本,若想從零開始手撕vue-ssr,能夠看上一篇文章從零開始搭建一個vue-ssr(上)。