Day 29: 实作 Vue 的 Server Side Render

这篇的程序码在 https://github.com/DanSnow/ironman-2020/tree/master/static-site-generator/packages/vue-ssr

这篇要来实际尝试 Vue 的 SSR ,这篇做的事很简单,就是在 server render 好一个 Vue 的程序,然後在 Client 端也把 js 加上去,程序的部份放在 App.vue 中,是个很简单的 counter ,我们直接来看其它部份,首先我们要准备一个 app.js ,里面 export 一个函式用来建立 Vue 的程序:

import Vue from 'vue'
import App from './App.vue'

export function createApp() {
  const app = new Vue({
    render: (h) => h(App),
  })
  return { app }
}

这边做最主要的原因是确保每次的 render 都会是新的程序,不会受到之前的状态影响之类的,如果有需要加上 store 等等的也要加在里面,目前先不加

再来我们要准备两个进入点,一个给 server 用来 render html,另一个是在 client 回复程序的状态时使用,它们分别叫 entry-server.jsentry-client.js,先是 entry-server.js:

import { createApp } from './app'

export default (context) => {
  const { app } = createApp()
  return app
}

目前就很简单的把 createApp 包装一下再 export 出去而已,再来是 entry-client.js ,这其实跟平常写 Vue 没什麽两样:

import { createApp } from './app'

const { app } = createApp()

app.$mount('#root')

再来要准备 webpack 的设定打包这两个档案,我们来写个 webpack.config.js,另外这篇用的是 webpack 5 ,原生就支援 PnP 感觉很方便:

const webpack = require('webpack')
const VueLoaderPlugin = require('vue-loader/lib/plugin')

const baseConfig = {
  module: {
    rules: [
      // vue loader 的设定
      {
        test: /\.vue$/,
        loader: 'vue-loader',
      },
    ],
  },
  plugins: [
    new VueLoaderPlugin(),
    // webpack 5 预设不再提供 node 的 API 了
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('development'),
    }),
  ],
  optimization: {
    // 开一下串接模组,这样产出来的档案会小一点,比较好看
    // 如果全都是 es6 module , webpack 5 甚至有可能编出不含 runtime 的 code 喔
    concatenateModules: true,
  },
  mode: 'none',
  devtool: false,
}

module.exports = [
  // server 的设定
  {
    ...baseConfig,
    entry: {
      server: './src/entry-server',
    },
    output: {
      // 要把 app export 出来,所以这边要设定 `libraryTarget`
      libraryTarget: 'commonjs2',
    },
    // 不需要打包进 vue
    externals: ['vue'],
    // 一定要指明是 `node` ,这同时会让 vue-loader 编出 SSR 用的 code
    target: 'node',
  },
  // client 的设定
  {
    ...baseConfig,
    entry: {
      client: './src/entry-client',
    },
  },
]

写完後就直接输入 yarn webpack 跑一下打包吧,最後是 server.js:

const { resolve } = require('path')
const express = require('express')
const { createRenderer } = require('vue-server-renderer')
const createApp = require('./dist/server').default

// vue-ssr-outlet 是 vue 的 renderer 会插入 render 好的 html 的位置
const template = `
<html>
  <head>
    <title>SSR test</title>
  </head>
  <body>
    <!--vue-ssr-outlet-->
    <script src="client.js"></script>
  </body>
</html>
`

const app = express()
const renderer = createRenderer({
  template,
})

app.use(express.static(resolve(__dirname, 'dist')))

app.get('/', async (req, res) => {
  const context = {}
  const app = createApp()
  // 第一个参数是一个 vue 的 instance ,第二个则是一个 object ,回传是一个 promise 包着 html
  const html = await renderer.renderToString(app, context)
  res.send(html)
})

app.listen(3000, () => {
  console.log('listen at http://localhost:3000')
})

就这样,可以在终端机中输入 yarn node server.js 试试了,应该可以在 http://localhost:3000 看到我们的网页,到这边基本的东西准备完了,下一篇我们再来把 vue-router 跟 Vuex 加进去,也顺便试一下 serverPrefetch 跟 bundle renderer 吧


<<:  JS读书笔记30天 - Day29 Vue的起手式——建立应用程序

>>:  Display - 金鱼都能懂的CSS必学属性

[Lesson24] Kotlin - 条件

if-else Kotlin的条件判断叙述比较特别,它能够用 if-else 赋值给变数 val r...

Spring Framework X Kotlin Day 30 Review

GitHub Repo https://github.com/b2etw/Spring-Kotlin...

小队快跑 - 是夹心饼乾 或是 承上启下

当在公司历经了三年左右的时间,若能在前面文章的概念中不断落实与反覆演练,加上三年的时间应该可以历经起...

[D27] 物件侦测(8)

接下来详细一点的说明 YOLOv4 的内部架构! 目标检测通常由以下几个部分组成: Input: 指...

[询问]网路分析仪

前辈们, 是否有推荐的网路分析仪呢? 目前察到LinkRunner™ G2 Network Auto...