Vue.js SSR Step by Step (3) - 改造 vue/webpack 腳手架

wheato發表於2017-11-10

通過前面兩篇文章,寫了一個簡單的支援 vue 同構的 webpack 配置,但是還沒有 dev server,不能熱更新和實時編譯,用於開發還是非常的麻煩。

Vue 官方的 webpack 腳手架只是針對客戶端的,功能強大,配置齊全。所以在這個官方腳手架的基礎上改了一個支援 #SSR# 的版本,在改的過程中參考了 GitHub - vuejs/vue-hackernews-2.0: HackerNews clone built with Vue 2.0, vue-router & vuex, with server-side rendering

改過後的原始碼在 Github 上,使用方式:vue init wheato/vue-ssr-boilerplate project-name。接下來簡單的說說這個修改的過程。

修改程式碼

這一部分主要是增加兩個入口 ,修改內容和之前文章還有官方文件一樣。如果有用到 vue-router 還要修改 route.js,具體修改可以看官方文件。有了 vue-router 我們就可以在元件裡面定義靜態方法,服務端呼叫注入資料。

// Foo.vue
<template>
  <div>
    <h2>Foo</h2>
  </div>
</template>

<script>
export default {
  preFetch(data) {
    console.log(data)
  },
  data () {
    return {}
  }
}
</script>複製程式碼
// entry-server.js
import { createApp } from './app.js'
export default context => {
  return new Promise((resolve, reject) => {
    const { app, router } = createApp()
    router.push('/foo')
    router.onReady(() => {
      const matchedComponents = router.getMatchedComponents()
      matchedComponents.forEach(p => {
        p.preFetch('資料')
      })
      resolve(app)
    }, reject)
  })
}複製程式碼

增加 server.js

server.js 基本上就是從 vue-hackernews-2.0 種複製過來的,做了一些刪減,使用 express,也可以使用 Koa。

修改 webpack.*.conf.js

webpack.prod.conf.js 的修改點:

  • webpack.prod.conf.js 改成 webpack.server.conf.js;
  • 去掉 html-webpack-plugin,用了 SSR,不需要在把資源自動注入 html 檔案中了;
  • 把壓縮 css 和 js 外掛放到條件判斷中,只有生產環境才壓縮檔案;
  • 修改入口;
  • 增加 process.env.VUE_ENV = ‘client’
  • 增加 vue-server-renderer client 端外掛。

增加 webpack.server.conf.js:

  • 在 client 基礎上去掉 CommonsChunkPlugin;
  • 修改入口檔案,和輸出檔案,編譯目標平臺;
  • 增加 vue-server-renderer server 端外掛;
  • 增加 extract-text-webpack-plugin,不把 css 編譯進 server 端中;
  • 增加 process.env.VUE_ENV = ‘server’

webpack.dev.conf.js 中增加 vue-server-renderer client 端外掛,修改入口為 entry-client.js。

修改 dev-server.js

dev-server.js 這部分修改挺大的, 其中 client 的配置使用 webpack.dev.conf.js。

Client 部分沒有太多的改動,必須要使用的兩個外掛 webpack-dev-middleware 和 webpack-hot-middleware。增加了 clientCompiler done 事件的回撥,把編譯好的 client-bundle 檔案儲存進 clientManifest 中,頁面重新整理的時候伺服器渲染就能同步到之前修改過的內容。

// dev middleware
var clientCompiler = webpack(clientWebpackConfig)
var devMiddleware = require('webpack-dev-middleware')(clientCompiler, {
  publicPath: clientWebpackConfig.output.publicPath,
  noInfo: true
})
app.use(devMiddleware)
clientCompiler.plugin('done', stats => {
  stats = stats.toJson()
  stats.errors.forEach(err => console.error(err))
  stats.warnings.forEach(err => console.warn(err))
  if (stats.errors.length) return
  clientManifest = JSON.parse(readFile(
    devMiddleware.fileSystem,
    'vue-ssr-client-manifest.json'
  ))
  update()
})
// hot middleware
var hotMiddleware = require('webpack-hot-middleware')(clientCompiler, { heartbeat: 5000 })
app.use(hotMiddleware)複製程式碼

Server 部分中,devMiddleware 的程式碼更新不到服務端,所以要新增 watch 事件,實時編譯 server-bundle。

// watch and update server renderer
var serverCompiler = webpack(serverWebpackConfig)
var mfs = new MFS()
serverCompiler.outputFileSystem = mfs
serverCompiler.watch({}, (err, stats) => {
  if (err) throw err
  stats = stats.toJson()
  if (stats.errors.length) return
  // read bundle generated by vue-ssr-webpack-plugin
  bundle = JSON.parse(readFile(mfs, 'vue-ssr-server-bundle.json'))
  update()
})
// read template from disk and watch
template = fs.readFileSync(templatePath, 'utf-8')
chokidar.watch(templatePath).on('change', () => {
  template = fs.readFileSync(templatePath, 'utf-8')
  console.log('index.html template updated.')
  hotMiddleware.publish({ action: 'reload' })
})複製程式碼

修改 build.js

修改點比較簡單,在 client 端編譯完後,再增加編譯 server 端就可以。最後修改一下 package.json 的命令。

npm run dev
npm run build
npm run server // 啟動伺服器複製程式碼

更多的程式碼可以使用腳手架模板建立一個空專案跑一下看看。

Vue.js SSR Step by Step 系列

相關文章