之前做过开发环境的webpack4.x迁移,但是由于一些原因后续生产的配置改造搁置了...近期正在着手处理这块,本文是对迁移过程的记录分享,包括一些新版本的处理方式和实际改写存在的问题。
想看之前开发环境配置记录的可以走该传送门。
下面开始正文...
踩坑
mode
上次的分享中有讨论过在新版本的webpack内,有约定大于配置一说,但是当时我配置的部分内容其实是有问题的(主要是没考虑production
,逃)。process.env.NODE_ENV
在默认情况下将会得到development
,在生产的config
文件内,我们可以通过直接设置mode
属性进行merge
,它会被关联到process.env.NODE_ENV
上。
let prodConfig = merge(baseConfig, {
mode: 'production',
// ...
});
老版本可以像下面这样设置:
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
url-loader
在配置生产环境打包文件的过程中,我发现之前处理文件输出的loader
写法也存在问题。因为最终我们的静态资源都要放在一个dist
目录下,并且通常是不会改变的(请结合自身实际业务场景)。那我需要的就是将url()
引到的文件,原封不动地输出到dist
中。之前的写法是怎么样的呢?
{
loader: 'url-loader',
options: {
limit: 1024,
outputPath: 'images'
}
}
这样处理会有两个问题:
- 文件打包时输出路径并不在
dist
下。我们调整时,需关注output
中的path
设置。 - 没有配置
name
。该属性会指定文件输出时的名称,缺省状态下会生成一串哈希值(不包含原文件名)。
改写:
{
loader: 'url-loader',
options: {
limit: 1024,
name: '[name].[ext]', // 原文件名.后缀 等价于输出原文件名
outputPath: './' // 结合path 定位输出目录为dist
}
}
DllReferencePlugin
这是一个归属于webpack
下的插件,通过如下方式配置,会检视manifest.json
中的映射关系略过已被处理的模块。与开发环境配置一文中的处理相同。
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./dist/vendor-manifest.json')
})
CleanWebpackPlugin
用于清理文件目录的插件,通常我们会在重新打包编译前清空你存放部署文件的目录,比如我们的dist
。引入方式务必注意,在webpack4.x
版本中,我们须要通过const { CleanWebpackPlugin } = require('clean-webpack-plugin')
的方式引用。
前文我们有讨论会先打一个dll
出来,而配置dll
时,我们已经清理了一次目录,在build
时,我们同样需要再清理一次之前可能打包过的旧内容,但像vendor.dll.js
、vendor.manifest.json
之类的dll
生成内容需要保留,我们可以结合该插件提供的生命周期属性cleanOnceBeforeBuildPatterns
介入:
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: ['**/*', '!vendor.dll.js', '!vendor-manifest.json', '!vendors~pdfjsWorker.dll.js'], // 数组格式,通过!保留你要的内容,第一个参数表明当前目录
})
CopyWebpackPlugin
当然,我们可能有些静态资源不是通过url
引用的,须要我们手动输出到dist
下,可以通过CopyWebpackPlugin
插件拷贝过去:
new CopyWebpackPlugin([
{
from: './public/',
to: './'
}
])
HtmlWebpackPlugin
这个跟开发环境的配置也类似,不过我们部署时须写入文件,并且要适当减小体积,操作如下:
new HtmlWebpackPlugin({
template: path.resolve(__dirname, './src/index.ejs'),
filename: 'index.html',
alwaysWriteToDisk: true, // 写入磁盘
chunks: ['vendor', 'index'], // 配置取决于你的分块内容,有分块加vendor
minify: {
removeComments: true, // 移注释
collapseWhitespace: true, // 移空格
removeAttributeQuotes: true // 移引号
}
})
optimization压缩优化
过去版本的webpack常采用UglifyJsPlugin
进行代码压缩。在webpack4.x中则改为通过在optimization
下配置minimizer
的方案实现:它接收一个数组,里面是我们使用的插件。通常都是TerserPlugin
:
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
output: {
comments: false, // 配合下面的extractComments,移除代码中注释
compress: {}, // 默认 设置false可以跳过压缩环节 想自己定制 见 https://github.com/terser/terser#compress-options
},
},
// sourceMap: true,
extractComments: false, // 不单独提取/^\**!|@preserve|@license|@cc_on/i规则的注释,并生成xx.js.LICENSE
cache: true, // 开启缓存
parallel: true, // 是否并发 设置为true 并发数为 os.cpus().length - 1 即你的内核数 - 1 也可以手动指定数字
}),
]
}
stats
这个属性是用来控制命令行输出统计内容的,默认情况下我们会看到一堆输出内容,非常冗余。事实上,我们只想看到最后输出了哪些文件以及报错时是什么问题,下面是我的配置:
stats: {
all: false, // 不输出全部信息
assets: true, // 输出最后的打包文件
errors: true, // 遇到错误时,输出内容
warnings: false, // 静默warning
moduleTrace: true, // 遇到错误时,定位文件
errorDetails: true, // 输出具体错误
}
比较
以上大致就是本人迁移中遇到的一些问题,下面贴一下操作前后的比较图...
原本脚手架自带的打包:

这里可能有同学会问这个File sizes after gzip
是什么,打出来的体积就是gzip
压缩体积吗?其实不是的,它只是在react-dev-utils
库中的一些方法帮助下计算了文件处理后的体积,并非是真实进行了处理。是否开启gzip
需要服务器的处理。
迁移魔改后:
- 打
dll
耗时:

- 生产
build
:

60s -> 20s ?
溜了溜了.