通過前面兩篇文章,寫了一個簡單的支援 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 // 啟動伺服器複製程式碼
更多的程式碼可以使用腳手架模板建立一個空專案跑一下看看。