项目背景简介
- 多页面应用,每个页面独立entry,单个页面内使用vue-router
- 基于vue,使用vue-loader加载.vue文件
- 单页内使用懒加载异步chunk
项目二期上线后,相对不辣么忙,眼看着webpack4也出来快半年了并且日趋稳定,之前虽然写过demo测试使用,但还未在项目中用过,于是择日不如撞日一鼓作气开启了升级【踩坑】之旅
升级之旅
首先保证 node>= 6.11.5,建议使用8以上LTS版本
相关依赖包安装或更新
以下是我的项目中使用到的依赖安装或更新情况,不同项目使用loader、插件不同,这部分得根据自身情况调整
主要依赖:
- webpack安装最新
- webpack-cli安装(启动webpack)
- webpack-dev-server更新至3以上
- vue-loader更新至15以上
- 安装mini-css-extract-plugin
其他:
babel相关依赖、html-webpack-plugin、file-loader、url-loader、sass-loader等我都升级了版本,或者也可以修改完配置后,运行起来看看控制台是否有相关报警再更新
辅助:
推荐安装webpack-bundle-analyzer插件,可以分析构建结果依赖关系
配置变更
接下来来到这次愉快踩坑之旅的重头戏了!
mode
mode
是webpack4新增的配置项,也是本次更新的亮点之一,简单理解就是告诉webpack本次构建模式,使其可以对构建使用对应的优化策略。回想之前写工程化配置,也会区分开发模式和生产模式的配置入口,webpack4提供了mode
降低了不少配置成本。
mode
可被设置为development
(默认)或production
module.exports = {
mode: 'production'
};
development
侧重于优化开发体验,production
侧重于优化模块体积和线上部署,具体优化内容这里就不搬运官方文档了,对于我的项目而言比较在意的有以下几点:
- 将自动设置
process.env.NODE_ENV
为对应值(development
或production
),原手工配置process.env.NODE_ENV = 'development' ;
的代码可以删除 -
production
模式将自动加入代码压缩功能,可删除原new UglifyJsPlugin()
相关代码
更多说明参考 https://medium.com/webpack/webpack-4-mode-and-optimization-5423a6bc597a
optimization
webpack4根据mode
配置对构建进行优化,也可以通过覆盖optimization
配置进一步定制两种构建模式下的优化策略,并且官方移除了CommonsChunkPlugin
插件。
其中项目升级最大的变更就在CommonsChunkPlugin
插件的移除和optimization
下splitChunks
、runtimeChunk
的配置
另外由于代码压缩功能将在production
模式下自动开启,如果需要对js压缩或css压缩策略定制,则需要覆盖默认optimization.minimizer
配置
splitChunks
关于splitChunks
可以单独用一篇文章进行详细说明,这里我只是按原项目的缓存策略修改(CommonsChunkPlugin
配置): 抽取/node_modules/下第三方依赖作为公共vendor(基于第三方依赖更新频率低)
默认配置下,splitChunks
只对懒加载的模块产生影响
注释部分是splitChunks的默认配置,这里仅修改cacheGroups内vendor相关配置,将chunks改成'initial'避免将部分异步加载的较大的第三方依赖也合并到vendor中
splitChunks: {
//chunks: 'async',
//minSize: 30000,
//minChunks: 1,
//maxAsyncRequests: 5,
//maxInitialRequests: 3,
//automaticNameDelimiter: '~',
//name: true,
cacheGroups: {
vendor: {
name: 'vendor',
//@NOTE 配置成all 会把async的也打进来
chunks: 'initial',
priority: -10,
test: /[\\/]node_modules[\\/]/
}
}
}
runtimeChunk
之前的production
模式构建会使用CommonChunkPlugin
抽取runtime code(这部分概念不了解的可以参考https://webpack.js.org/concepts/manifest/#src/components/Sidebar/Sidebar.jsx)作为单独的chunk,升级后这部分配置需要写到optimization.runtimeChunk
中:
- 默认设置为
false
,runtime code包含在基于各个entry抽取的第一个chunk中(注意:由于本项目抽取了公用vendor,针对本项目会将所有entry的runtime代码抽取到公用vendor中,vendor将失去浏览器缓存意义) - 配置为
single string
,所有entry的runtime代码将会抽取为一个chunk - 配置为一个生成函数,可以基于每个entry独立抽取runtime代码(本项目使用,多个独立页面多入口)
配置成生成函数的代码:
runtimeChunk: {
name: entrypoint => `manifest~${entrypoint.name}`
},
minimizer
这个接收单个对象或对象数组,指定压缩插件和配置,相对比较简单,这里就不详细描述了
minimizer: [
// 配置UglifyJsPlugin压缩js文件
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: true
}),
// 配置css文件压缩
new OptimizeCSSAssetsPlugin({
cssProcessor: require('cssnano'),
cssProcessorOptions: {
discardComments: {removeAll: true},
// 避免 cssnano 重新计算 z-index
safe: true
},
canPrint: false
})
]
mini-css-extract-plugin
webpack4之后,Extract-text-plugin
不再适用于css文件抽取,如果还要继续使用安装时只能通过@next版本安装(yarn add extract-text-plugin@next),除此之外contenthash
不支持使用在文件名中、会生成一些多余的chunk css文件
mini-css-extract-plugin
专用于抽取css文件支持async-loading,只能配合webpack4使用,但目前暂时还不支持HMR(Hot Module Replace),本项目只在production
下使用这个插件
配置相对Extract-text-plugin
是简单一些
// loader部分(项目使用scss)
test: /\.(sa|sc|c)ss$/,
use: [
isProd ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
... ...
}
},
],
},
// plugin部分
plugins.push(
new MiniCssExtractPlugin({
filename: isProd ? "css/[name].[contenthash:8].css" : "css/[name].css";
})
);
vue-loader
vue-loader配合webpack4升级到了v15版本,升级之后配置方式发生了一些改变,以下是项目中使用到的部分:
- 增加一个步骤:需要以plugin方式添加到webpack配置中
- v15版本之后,使用了不同的策略推导.vue文件中各个语言块使用的loader,将各个语言块视为独立的文件使用webpack中配置了规则的loader处理,由此带来的配置变化就是针对样式处理,webpack.rules中必须显示提供对应loader处理的规则,原vue-loader配置中内联传入的样式相关loader可以去除
- 鉴于推导变化,
<script></script>
标签内的js代码将被视为独立的js文件并根据webpack配置使用babel-loader转译;项目配置babel-loader时使用exclude: /node_modules/
排除依赖包中代码的转译,如果导入了/node_modules/
中的.vue文件,<script>
部分将不能被转译,故需要将.vue文件加入到排除白名单中
// 增加一个plugin
const VueLoaderPlugin = require('vue-loader/lib/plugin')
plugin.push( new VueLoaderPlugin())
// rules loader配置
// @UPDATED
// vue-loader v15+版本 .vue文件中的样式将被抽取出来并认为和独立引入的css文件相同
// 故需要配置单独loader处理
... ...
{
test: /\.(sa|sc|c)ss$/,
use: [
isProd ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
includePaths: ['src/static/scss']
}
},
],
},
// .vue文件
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
// @UPDATED @DEPRECATED
// v15+版本不再需要提供内联的cssloader配置
// 如果不去掉会报错
// loaders: util.cssLoaders({
// sourceMap: false,
// extract: build,
// build: build
// })
}
},
// .js文件
{
test: /\.js$/,
loader: 'babel-loader',
exclude: file => (
// @UPDATED
// vue-loader v15+版本
// /node_modules/中的.vue文件需要经过babel-loader转译
/node_modules/.test(file) &&
!/\.vue\.js/.test(file)
)
}
... ...
其他变更可以参考vue-loader官方文档说明https://vue-loader.vuejs.org/migrating.html#notable-breaking-changes
DEMO
项目webpack4配置demo,可以参考~~
https://github.com/icyfanfan/try-webpack4
参考
webpack官方文档
https://medium.com/webpack/webpack-4-mode-and-optimization-5423a6bc597a
https://medium.com/webpack/webpack-4-import-and-commonjs-d619d626b655
https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
mini-css-extract-plugin
https://github.com/webpack-contrib/mini-css-extract-plugin
vue-loader
https://vue-loader.vuejs.org/migrating.html#notable-breaking-changes