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

2,591 阅读3分钟

通过前面两篇文章,写了一个简单的支持 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 系列