webpack优化

72 阅读6分钟
webpack做为一款当下最流行的前端构建工具,一直以来以门槛太高而受人诟病,且没有一个通用的配置适合所有的项目,为此我们不得不开心的(大误:smiley:)手动配置我们的项目,开启我们的配置工程师之路,本文主要是对webpack的一些基础配置的解释和我们能做的一些优化工作,适合有使用过webpack的前端开发者阅读,各位在阅读过程中不妨打开自己的项目来跟着我一起优化。
分割配置文件 dev prod通常我们的项目会有开发环境和生产环境,而开发环境我们配置的目标是构建更快,模块热替换,能从chrome控制台报错信息对应的源码的错误处(source map)等。生产环境我们更加关注chunk分离,缓存,安全,tree shaking等优化点。当然对于开发和生产环境的配置文件肯定是不完全相同的,所以我们将不同环境的配置文件分离开,同时将共用部分抽出来,最后使用webpack-merge插件整合。
[JavaScript]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
//参考使用方法
const webpackMerge = require('webpack-merge');
const additionalConfigPath = {
development: './webpack.dev.config.js',
production: './webpack.prod.config.js'
};
const baseConfig = {};
module.exports = webpackMerge(
baseConfig,
require(additionalConfigPath[process.env.NODE_ENV])
);

在下方的优化点中 我会标注这些点适用的环境 dev=开发环境 prod=生产环境

代码分离 prod代码分离能够将工程代码分离到各个文件中,然后按需加载或并行加载这些文件,也用于获取更小的 bundle,以及控制资源加载优先级
webpack有三种常用的代码分离方法:
  • 入口起点:配置多入口,输出多个chunk。
  • 防止重复:使用 CommonsChunkPlugin 去重和分离 chunk(webpack4虽然废弃,但有代替方法)
  • 动态导入:常用于按需加载的模块分离


[JavaScript]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
//多入口配置 最终输出两个chunk
module.exports = {
entry: {
index: 'index.js',
module: 'module.js'
},
output: {
//对于多入口配置需要指定[name]否则会出现重名问题
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};



问题所在:入口chunks中如果包含重复的模块,如jquery,那些重复模块都会被引入到各个bundle中

去除重复
对于webpack4 < 4 我们使用CommonsChunkPlugin插件
[JavaScript]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
entry: {
"app": "entry-client.js"
}
//首先将app入口的公共模块提取出来
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
filename: '[name].[chunkHash:6].common.js',
chunks: ['app']
})
//将 vendor runtime分离出来
new webpack.optimize.CommonsChunkPlugin({
name: ['vendor','runtime'],
filename: '[name].[chunkHash:6].bundle.js',
//这个配置保证没其它的模块会打包进 vendor chunk
minChunks: Infinity
}),
chunks 选项代表了从哪个入口分离公共文件,这里我们使用我们项目的主入口 app来分离自定义的公共模块
首先将自定义的公共模块单独抽离出来,这样做的目的是方便我们做缓存,当自定义模块更新时不会影响到vendor文件的hash值。
然后将第三方库文件 和 webpack运行文件分离。
这样我们的vendor就是非常干净的了,只包含第三方库,可以做长效缓存。这个地方需要分离webpack运行文件runtime,因为无论我们是否修改了项目文件,每次build项目时的runtime文件的hash值总是会发生变化的,需要单独打出来。
对于webpack4 我们需要实现同样的目标

[JavaScript]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 提前自定义公共模块
optimization: {
splitChunks: {
chunks: "all",
minSize: 20000,
//其他入口chunk引用的次数
minChunks: 1,
//默认使用name + hash生成文件名
name: true,
//使用自定义缓存组
cacheGroups: {
//公共模块
commons: {
name: 'common',
//缓存优先级设置
priority: 10,
//从入口chunk提取
chunks: 'initial'
},
//提取第三方库
vendors: {
//符合条件的放入当前缓存组
test: /[\\/]node_modules[\\/]/,
name: "vendors",
chunks: "all"
},
}
}
}
//提取webpack运行文件
runtimeChunk: {
name: 'manifest'
},


require.ensure 现在依赖于原生的 Promise。如果在不支持 Promise 的环境里使用 require.ensure,你需要添加 polyfill。
普通组件的异步加载既然我们可以使用import()和require.ensure来动态导入组件那其实在项目中我们更是做到可以基于组件的异步加载。
比如我们的首页是由20个组件组成,但是首屏只会显示15个组件,另外的5个组件可能是一些弹窗,或底部才显示的这种组件,那么我们完全可以将之使用动态导入抽离出来,在需要的时候加载它,如:


[JavaScript]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
//在异步事件中去导入组件
<div onClick={this.showPopUps}>
show-popUps
</div>
showPopUps = () => {
this.popUps = ()=> {
import(/*webpackChunkName: 'PopUps'*/'./popUps')
}
}

这样处理的问题在于,我们在点击按钮弹出浮层时会有一定的延时,因为在点击时我们才从服务端去获取组件,用户体验可能不太优秀。
这时我们可以使用一种预测加载的技术prefetch。prefetch提示浏览器这个资源将来可能需要,浏览器会选择==空闲的时间==去下载此资源。prefetch常常用于加速下一次操作。
<link rel="prefetch">
兼容性问题:从上述可以得知,prefetch时基于浏览器实现的,存在一定的兼容性问题,在safari上还没由得到支持。
在不支持的浏览器中并不会影响我们页面的功能
使用:preload-webpack-plugin使用条件:webpack > 2.2 且需要使用 html-webpack-plugin

[JavaScript]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
plugins: [
new HtmlWebpackPlugin(),
new PreloadWebpackPlugin({
rel: "prefetch",
as: 'script',
//包含了哪些chunk,默认值为"asyncChunks"
include: 'asyncChunks'
})
]
//最终效果类似这样
<link rel="prefetch" as="script" href="chunk.d15e.js">

对于.css结尾的文件 as=style,.woff2结尾的文件as=font,否则as=script,当然你可以像上方那样给一个确定的值。
对于include的值,其实我们并不是需要所有的异步chunk都使用prefetch,所以我们需要重设include的值,像这样:


[JavaScript]
纯文本查看
复制代码
1
2
3
4
5
6
7
//1 给定需要prefetch的块的名字(chunkName)
include: ["PopUps","Login"]
//2 或者在动态导入时指定Prefetch
this.popUps = () => {
import(/* webpackChunkName: 'PopUps', webpackPrefetch: true */'./PopUps')
}

引用自 juejin.cn/post/684490…