Koa2+Vue SSR+webpack热更新的详细配置
由于最近做博客前台需要改界面,不能每次修改就重新打包一次吧,太费时费力了,索性我就花了2天时间,研究了一下基于koa2的webpack热更新。
本文主要讲解setup-dev-middleware.js的配置和使用
需要用到的第三方依赖包
Koa-webpack-dev-middleware
koa-webpack-hot-middleware
-
koa-convert
koa-convert
是必须的,因为Koa-webpack-dev-middleware
和koa-webpack-hot-middleware
是koa1的插件,需要进行转换
配置koa版本的setup-dev-middleware.js
-
定义服务器端bundle和浏览器端manifest
let bundle; let template = fs.readFileSync(templatePath, 'utf-8'); let clientManifest; //update检测到有更新后,调用传递过来的回调函数,一般是createBundleRenderer这个函数。 const update = () => { if (bundle && clientManifest) { callback(bundle, { template, clientManifest }) } }
-
修改client端入口配置文件,注意配置文件需要
clientConfig.entry.app = ['webpack-hot-middleware/client?path=/__webpack_hmr&timeout=2000&reload=true', clientConfig.entry.app] clientConfig.output.filename = '[name].js' clientConfig.plugins.push( new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin() )
-
配置koa-webpack-dev-middleware,这里需要传递浏览器端的
Compiler
注意:这里使用
devMiddleware
的时候需要先调用convert()
把koa1的包转成koa2可以用的包const clientCompiler = webpack(clientConfig) const devMiddleware = webpackDevMiddleware(clientCompiler, { publicPath: clientConfig.output.publicPath,//必选参数,定义启动后的默认访问路径,填错可导致404 noInfo: true,//可选 stats: {//可选 colors: true, modules: false, }, }) app.use(convert(devMiddleware))
-
配置订阅热更新
注意:这里使用
webpackHotMiddleware
也需要包裹convert()
// hot update 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 console.log('\nclient-dev...\n') clientManifest = JSON.parse(readFile( devMiddleware.fileSystem, 'vue-ssr-client-manifest.json' )) update() }) // hot middleware app.use(convert(webpackHotMiddleware(clientCompiler)))
-
配置服务端热更新
const serverCompiler = webpack(serverConfig) const mfs = new MFS() serverCompiler.outputFileSystem = mfs serverCompiler.watch({}, (err, stats) => { if (err) throw err stats = stats.toJson() if (stats.errors.length) return console.log('server-dev...') bundle = JSON.parse(readFile(mfs, 'vue-ssr-server-bundle.json')) update() })
完整代码
const path = require('path')
const MFS = require('memory-fs')
const webpack = require('webpack')
const clientConfig = require('./webpack.client.config')
const serverConfig = require('./webpack.server.config')
const webpackDevMiddleware = require('koa-webpack-dev-middleware')
const webpackHotMiddleware = require('koa-webpack-hot-middleware')
const convert = require('koa-convert')
const fs = require('fs');
const opn = require('opn')
const readFile = (fs, file) => fs.readFileSync(path.join(clientConfig.output.path, file), 'utf-8')
module.exports = function setupDevServer(app, uri, callback) {
let bundle;
let template = fs.readFileSync(uri, 'utf-8')
let clientManifest
const update = () => {
if (bundle && clientManifest) {
callback(bundle, {
template,
clientManifest
})
}
}
// client
clientConfig.entry.app = ['webpack-hot-middleware/client?path=/__webpack_hmr&timeout=2000&reload=true', clientConfig.entry.app]
clientConfig.output.filename = '[name].js'
clientConfig.plugins.push(
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
)
// webpack config
const clientCompiler = webpack(clientConfig)
// dev middleware
const devMiddleware = webpackDevMiddleware(clientCompiler, {
publicPath: clientConfig.output.publicPath,
noInfo: true,
stats: {
colors: true,
modules: false,
},
})
app.use(convert(devMiddleware))
// hot update
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
console.log('\nclient-dev...\n')
clientManifest = JSON.parse(readFile(
devMiddleware.fileSystem,
'vue-ssr-client-manifest.json'
))
update()
})
// hot middleware
app.use(convert(webpackHotMiddleware(clientCompiler)))
// server
const serverCompiler = webpack(serverConfig)
const mfs = new MFS()
serverCompiler.outputFileSystem = mfs
serverCompiler.watch({}, (err, stats) => {
if (err) throw err
stats = stats.toJson()
if (stats.errors.length) return
console.log('server-dev...')
bundle = JSON.parse(readFile(mfs, 'vue-ssr-server-bundle.json'))
update()
})
devMiddleware.waitUntilValid(() => {
console.log('\n> Listening at http://localhost:3001' + '' + '\n')
opn('http://localhost:3001/');
})
}
调用setup-dev-middleware
const setupDevServer = require('./build/setup-dev-middleware');
let renderer;
setupDevServer(app, './template.html', (bundle, options) => {
try {
renderer = createBundleRenderer(bundle, options)
} catch (e) {
console.log('\nbundle errorasdasd', e)
}
})
const renderData = (ctx, renderer) => {
const context = {
url: ctx.url
}
return new Promise((resolve, reject) => {
renderer.renderToString(context, (err, html) => {
if (err) {
return reject(err)
}
resolve(html)
})
})
}
app.use(async (ctx, next) => {
let html, status;
try {
status = 200
html = await renderData(ctx, renderer)
} catch (e) {
console.log('\ne', e)
if (e.code === 404) {
status = 404
html = '404 | Not Found'
} else {
status = 500
html = '500 | Internal Server Error'
}
}
ctx.type = 'html'
ctx.status = status ? status : ctx.status
ctx.body = html
})
有疑问可以在下方评论或者联系我哈!
下一片预告:《使用tocbot根据html生成目录文档toc优化文章阅读效果(html由markdown生成)》
欢迎关注我的个人博客,第一时间发布最新内容:小新的个人博客,完整源码在我的github:完整源码