概述
最近非常想做一个服务端渲染项目,那就打算从尤大的vue-hackernews-2.0开始入手呗。其实我之前试图改造过这个项目,但是因为当时很菜所以失败了。现在我觉得有能力改造好,那就开始呗。把心得记录下来,供以后开发时参考,相信对其他人也有用。
mode
webpack4带来的第一个大变化就是增加了mode字段来区分生产环境还是开发环境,所以我们到webpack.base.config.js
里面去增加mode字段:
mode: isProd ? 'production' : 'development',
package.json
老项目的依赖如下:
"dependencies": {
"compression": "^1.7.1",
"cross-env": "^5.1.1",
"es6-promise": "^4.1.1",
"express": "^4.16.2",
"extract-text-webpack-plugin": "^3.0.2",
"firebase": "4.6.2",
"lru-cache": "^4.1.1",
"route-cache": "0.4.3",
"serve-favicon": "^2.4.5",
"vue": "^2.5.22",
"vue-router": "^3.0.1",
"vue-server-renderer": "^2.5.22",
"vuex": "^3.0.1",
"vuex-router-sync": "^5.0.0"
},
"devDependencies": {
"autoprefixer": "^7.1.6",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-preset-env": "^1.6.1",
"chokidar": "^1.7.0",
"css-loader": "^0.28.7",
"file-loader": "^1.1.5",
"friendly-errors-webpack-plugin": "^1.6.1",
"extract-text-webpack-plugin": "^3.0.2",
"rimraf": "^2.6.2",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.1",
"sw-precache-webpack-plugin": "^0.11.4",
"url-loader": "^0.6.2",
"vue-loader": "^15.3.0",
"vue-template-compiler": "^2.5.22",
"webpack": "^3.8.1",
"webpack-dev-middleware": "^1.12.0",
"webpack-hot-middleware": "^2.20.0",
"webpack-merge": "^4.2.1",
"webpack-node-externals": "^1.7.2"
}
我们删除一下插件,用更新的插件来代替他:
// 删除
"babel-core": "^6.26.0",
"extract-text-webpack-plugin": "^3.0.2",
// 添加
"@babel/cli": "^7.11.6",
"@babel/core": "^7.11.6",
"@babel/preset-env": "^7.11.5",
"mini-css-extract-plugin": "^1.0.0",
// webpack4 需要安装 webpack-cli
"webpack-cli": "^3.3.12",
然后重新安装所有其它依赖,让它们达到最新版本。
firebase
尤大的这个项目是使用 firebase 来获取 hackernews 的数据的,我本来想用 axios 取代的,但是我去看了一下hackernews 的 api之后发现,尤大的 firebase 的写法只需要少量代码就能够获取全部数据,如果使用 axios 的话,就需要先去请求一个总接口获取文章 id,然后再用这些 id 去一篇篇获取每篇文章的数据,非常麻烦,所以这里就没有升级到 axios。
另外,我去看了一下最新版本的 firebase 的 api,发现使用方法和尤大那个版本的使用方法是一样的,所以这里只需要升级 firebase 到最新版本即可。
lru-cache
lru-cache 升级到最新版本之后需要使用 new 语法:
// 旧的语法
api.cachedItems = LRU({
max: 1000,
maxAge: 1000 * 60 * 15 // 15 min cache
})
// 新的语法
api.cachedItems = new LRU({
max: 1000,
maxAge: 1000 * 60 * 15 // 15 min cache
})
babel
老项目使用的 babel-core 现在已经不建议使用了,用@babel/preset-env
替代,然后在 babel-loader 的 presets 里面加入@babel/preset-env
:
{
test: /.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
presets: ['@babel/preset-env']
}
}
css
webpack4 摈弃了 extract-text-webpack-plugin 而使用新的 mini-css-extract-plugin 插件,我们注释掉上一个插件的代码,然后使用新的 mini-css-extract-plugin 插件。
但是这个插件有一个问题,就是它在 extract css 文件的时候会使用 document 方法,从而导致 ssr 的服务端打包的时候会报document is not defined
的错误,具体可以看这里
解决方案推荐使用 null-loader,原理是在服务端打包的时候,不处理 css,这就要求客户端和服务端使用不同的配置,代码如下:
// webpack.client.config.js
module: {
rules: [
{
test: /.styl(us)?$/,
use: [
isProd ? {
loader: MiniCssExtractPlugin.loader,
options: {
esModule: false,
},
} : 'vue-style-loader',
{
loader: 'css-loader',
options: {
esModule: false,
}
},
'stylus-loader'
],
},
],
}
plugins: [
new MiniCssExtractPlugin({
filename: 'common.[chunkhash].css'
}),
]
// webpack.server.config.js
module: {
rules: [
{
test: /.styl(us)?$/,
use: 'null-loader'
}
]
},
由于此项目是使用 stylus 的,所以如果使用 scss 等的话可以类似处理。