概述
最近非常想做一个服务端渲染项目,那就打算从尤大的vue-hackernews-2.0开始入手呗。其实我之前试图改造过这个项目,但是因为当时很菜所以失败了。现在我觉得有能力改造好,那就开始呗。把心得记录下来,供以后开发时参考,相信对其他人也有用。
上篇:vue-hackernews-2.0 升级到 webpack4 和 nodejs14 的坑(上)
CommonsChunkPlugin
老项目是使用 commonchunksplugin 抽取公共模块和css模块的,代码如下:
// extract vendor chunks for better caching
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module) {
// a module is extracted into the vendor chunk if...
return (
// it's inside node_modules
/node_modules/.test(module.context) &&
// and not a CSS file (due to extract-text-webpack-plugin limitation)
!/.css$/.test(module.request)
)
}
}),
// extract webpack runtime & manifest to avoid vendor chunk hash changing
// on every build.
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest'
}),
而 webpack4 则直接内置了 optimization 字段来实现这个功能,所以我们注释掉上面的代码,然后加入如下代码:
// webpack.client.js
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
styles: {
name: 'styles',
test: /css$/,
enforce: true,
},
vendor: {
name: 'vendor',
test: /[/]node_modules[/]/,
enforce: true,
},
},
},
runtimeChunk: {
name: 'manifest',
},
},
externals
老项目是使用 externals 字段在服务端打包的时候防止 css 没有被引入,但是这里我们已经在服务端不打包 css 了,所以这个配置可以删去。但是这里我们加上如下配置,来防止引入 node_modules 里面的包,从而提高打包效率:
// webpack.server.config.js
externals: Object.keys(require('../package.json').dependencies),
service-worker
老项目是使用sw-precache-webpack-plugin来安装 service-worker 的,现在这个包已经被workbox-webpack-plugin代替了,相关文档在这里
我们首先卸载 sw-precache-webpack-plugin 然后安装 workbox-webpack-plugin:
// 卸载
"sw-precache-webpack-plugin": "^0.11.4",
// 安装
"workbox-webpack-plugin": "^5.1.4"
然后我们在webpack.client.config.js
里面删掉 SWPrecachePlugin 的代码,加入 WorkboxPlugin 的相关配置:
// webpack.client.config.js
const WorkboxPlugin = require('workbox-webpack-plugin');
if (process.env.NODE_ENV === 'production') {
config.plugins.push(
new WorkboxPlugin.GenerateSW({
cacheId: 'vue-hn',
swDest: 'service-worker.js',
clientsClaim: true,
skipWaiting: true,
dontCacheBustURLsMatching: /./,
exclude: [/.map$/, /.json$/],
runtimeCaching: [
{
urlPattern: '/',
handler: 'NetworkFirst'
},
{
urlPattern: //(top|new|show|ask|jobs)/,
handler: 'NetworkFirst'
},
{
urlPattern: '/item/:id',
handler: 'NetworkFirst'
},
{
urlPattern: '/user/:id',
handler: 'NetworkFirst'
}
]
})
)
}
由于 service-worker 支持在 localhost 进行调试,所以我们更改一下老项目引入 service-worker 的代码:
// entry-client.js
if ('serviceWorker' in navigator && process.env.NODE_ENV === 'production') {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js').then(registration => {
console.log('SW registered: ', registration);
}).catch(registrationError => {
console.log('SW registration failed: ', registrationError);
});
});
}
虽然到这里就已经替换完成了,但是在实际打包之后,WorkboxPlugin 会生成一个带有hash的workbox.js
文件,当我们更改 WorkboxPlugin 相关配置之后再打包,这个 hash 值会变化。这里面有一个问题就是,这个文件没有被 express 暴露出来,所以请求不到这个文件(express 只暴露了没带 hash 的 service-worker.js文件)。
解决方法有2种,第一种是直接改entry-client.js
里面的service-worker
路径,在前面加上 dist。这样service-worker.js
在请求 workbox.js 的时候回自己带上 dist 前缀。代码如下:
// entry-client.js
if ('serviceWorker' in navigator && process.env.NODE_ENV === 'production') {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/dist/service-worker.js').then(registration => {
console.log('SW registered: ', registration);
}).catch(registrationError => {
console.log('SW registration failed: ', registrationError);
});
});
}
另一种解决方法是在 express 里面动态引入带有hash的workbox.js
文件。由于这个文件带有 hash 值,所以它不能被 express.static 直接引入,app.use 也不支持正则引入,所以我们需要获取 dist 文件夹下面的所有文件名,找出带有 workbox.js 的文件名,然后利用这个文件名通过 express.static 暴露出来,代码如下:
// workbox dir
const distFiles = fs.readdirSync('./dist')
const workboxDir = distFiles.filter(name => /workbox-.*.js$/.test(name))
if (workboxDir.length > 0) {
app.use(`/${workboxDir[0]}`, serve(`./dist/${workboxDir[0]}`))
}
完工
到这里就全部升级完毕了,详细代码可以参考我的vue-hackernews-2.0项目
我学到了什么:
- 了解了一下 webpack 在 ssr 的各种配置
- 尝试了一些 node api
- 为以后编写cheer-fun的项目做准备