从零开始搭建一个vuessr下

35次阅读

共计 4666 个字符,预计需要花费 12 分钟才能阅读完成。

从零开始搭建一个 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 dev
npm run server

浏览器打开 localhost:8082,我们便可以看到由 vue-cli 改造而成的 vue-ssr。

总结

本文是直接基于 vue-cli 进行改造的一个 ssr 版本,若想从零开始手撕 vue-ssr,可以看上一篇文章从零开始搭建一个 vue-ssr(上)。

项目源码

https://github.com/TheWalking…

正文完
 0