vue-cli3 构建 SSR 附件

创建 server 服务端

基于 vue-cli3 初始化项目

  1. /server/index.js 服务端

    const express = require('express');
    const server = express();
    const fs = require('fs');
    const { resolve } = require('path');
    const { createBundleRenderer } = require('vue-server-renderer');
    const serverBundle = require('../dist/vue-ssr-server-bundle.json');
    const clientManifest = require('../dist/vue-ssr-client-manifest.json');
    
    server.use(express.static(resolve('../dist'), { index: false }));
    
    
    server.get('*', async (req, res) => {
      // /demo, req.url /demo
      try {
        const url = req.url;
        // 2. 创建渲染器
        const render = createBundleRenderer(serverBundle, {
          template: fs.readFileSync('./index.ssr.html', 'utf-8'),
          clientManifest
        });
        const html = await render.renderToString({url});
        res.send(html)
        // 3. 利用渲染器将vue实例转化成html字符串
      } catch (error) {
        console.log(error);
        if (error.code == 404) {
          res.status(404).send('页面去火星了,找不到了,404啦');
          return;
        }
        res.status(500).send('服务器错误');
      }
    
      // res.send('hello');
    });
    
    server.listen(12306, () => console.log('server is run at 12306'));
    
  2. /server/index.ssr.html 模板

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
    </head>
    <body>
      <!--vue-ssr-outlet-->
      <!-- <script src="/client.bundle.js"></script> -->
    </body>
    </html>
    

创建客户端、服务端入口文件

  1. /src/entry/client.entry.js 客户端入口文件

    import createApp from '../main.js';
    
    const { app, store } = createApp();
    app.$mount('#app');
    
    if (window.__INITIAL_STATE__) {
      store.replaceState(window.__INITIAL_STATE__);
    }
    
  2. /src/entry/server.entry.js 服务端入口文件

    import createApp from '../main.js';
    
    export default function (ctx) {
      return new Promise((resolve, reject) => {
        const { app, router, store } = createApp();
        router.push(ctx.url);
        router.onReady(() => {
          // 判断当前路由下是否存在组件
          const matchedComponents = router.getMatchedComponents();
          if (matchedComponents.length == 0) {
            return reject({ code: 404 });
          }
    
          Promise.all(matchedComponents.map(c => {
            if (c.asyncData) {
              return c.asyncData(store)
            }
          })).then(() => {
            // window.__INITIAL_STATE__
            ctx.state = store.state;
            resolve(app);
          }).catch(reject);
          
          // resolve(app);
        }, reject)
      });
    }
    

修改客户端入口文件、根组件

  1. main.js

    import Vue from 'vue'
    import App from './App.vue'
    import createRouter from './router'
    import createStore from './store'
    
    Vue.config.productionTip = false
    
    export default function () {
      const router = createRouter();
      const store = createStore();
      const app = new Vue({
        router,
        store,
        render: h => h(App)
      });
      return {
        app,
        router,
        store,
      }
    }
    
  2. App.vue

    <template>
      <div id="app">
        <div id="nav">
          <router-link to="/">Home</router-link> |
          <router-link to="/about">About</router-link>
        </div>
        <router-view/>
      </div>
    </template>
    

修改 router、vuex

  1. /src/router/index.js 路由

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import Home from '../views/Home.vue'
    
    Vue.use(VueRouter)
    
    const routes = [
      {
        path: '/',
        name: 'Home',
        component: Home
      },
      {
        path: '/about',
        name: 'About',
        // route level code-splitting
        // this generates a separate chunk (about.[hash].js) for this route
        // which is lazy-loaded when the route is visited.
        component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
      }
    ]
    
    export default function () {
      const router = new VueRouter({
        mode: 'history',
        base: process.env.BASE_URL,
        routes
      });
      return router;
    }
    
  2. /src/store/index.js vuex

    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    export default function () {
      return new Vuex.Store({
        state: {
        },
        mutations: {
        },
        actions: {
        },
        modules: {
        }
      })
    }
    

修改配置文件

  1. /vue.config.js

    const VueSSRClientPlugin = require('vue-server-renderer/client-plugin');
    const VueSSRServerPlugin = require('vue-server-renderer/server-plugin');
    const TARGET_NODE = process.env.WEBPACK_TARGET === 'node';
    const target = TARGET_NODE ? 'server' : 'client';
    
    module.exports = {
      configureWebpack: () => ({
        entry: {
          app: `./src/entry/${target}.entry.js`
        },
        target: TARGET_NODE ? 'node' : 'web',
        output: {
          libraryTarget: TARGET_NODE ? 'commonjs2' : undefined
        },
        plugins: [TARGET_NODE ? new VueSSRServerPlugin() : new VueSSRClientPlugin]
      }),
      chainWebpack: config => {
        // 关闭多入口,否则报错
        config.optimization.splitChunks(undefined)
      }
    }
    
  2. package.json

    "scripts": {
      "serve": "vue-cli-service serve",
      "build": "vue-cli-service build",
      
      "lint": "vue-cli-service lint",
      "build:client": "vue-cli-service build",
      "build:server": "cross-env WEBPACK_TARGET=node vue-cli-service build --mode server",
      "build:all": "npm run build:server && move dist\\vue-ssr-server-bundle.json bundle && npm run build:client && move bundle dist\\vue-ssr-server-bundle.json"
    
      // build:all:
      //  - 先打包服务端,再把打包后的 dist 下服务端代码放到项目根路径下
      //  - 再打包客户端代码,再把根路径下的服务端代码放回 dist 下
    },
    
打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. vue-cli3 构建 SSR 附件
  2. 2. 创建 server 服务端
  3. 3. 创建客户端、服务端入口文件
  4. 4. 修改客户端入口文件、根组件
  5. 5. 修改 router、vuex
  6. 6. 修改配置文件