前两篇文章中,主要是讲解了 Webpack 的配置,但是随着项目越来越大,构建速度可能会越来越慢,构建出来的js的体积也越来越大,此时就需要对 Webpack 的配置进行优化。
如果你还没有看过前两篇,推荐先阅读《带你深入解锁Webpa * / h Lck系列(基础篇) 》和 《带你深入解锁We ~ A lebpackN w # 8 x p _ j d系列(进阶篇) 》
本文罗列出了Q m _ 0 = n @十多种优化方式,大家可以结合自己的项目,选择适当的方式进行优化。这些 Webpack 插件的源码我大多也没有看过,主要是结合 Webpack 官方文档以及项目实践,并且花了大量的时间验证后输出了本文,如果文中有错误的地方,欢迎$ W B ! G O E在评论区指正。
鉴于前端技术a d -变更迅速,u = y { b ~ A j N祭出本篇文章基于 Webpack 的版本号:
├── webpack@4.41.5
└── webpack-cli@3.K i d Q ; Q H3.10
本文对应的项目地址(编写本文时使用)供参考:https://github.com/YvetteLau/webpack/tree/master/webpack-optimize
量化
有时,我们以为的优化是 Y - Q d R R i负优化,这时,如果有一个量化的指标可以看出前后对比,那将会是再好不过的一件事。
spq i t I M Reed-measure-webpack-plugin 插件可以测量各个插件和loader所花费的时间,使用之后,构建时, t }会得到类似下面这样的信息:
对比前后的信息,来确定优化的效果。
speed-measure-webpack-pk p 2 r F b |lugin 的使用很简单,可以直接用其来包裹 Webpack 的配置:
//webpack.config.js
const SpeedMeasurePlugin = require(\"speed-measure-webpack-plugin\");
const&g v [ & 0 Snbsp;smp = new SpeedMeasurePlugin();
const config 5 ) E R Y 8 * 2 v;= {
 z H H $ w L r;//...webpacm C , * & e #k配置
}
module.exports = smp.wrap(config);
1.exclude/include
我们r l 可以通过 exclude、include 配置来确保转译尽可能少的文件。顾名思义: 7 D a,exclude7 e v ( Z c 指定要排除的文件,include 指定要包含的文件。
exclude 的A Z 7 K : (优先级高于 include,在 include 和 exclude 中使用绝对路径数组,尽量避免 exclude,更倾向于使用 include。
//webpacG # / # J + 6 Rk.config.js
cH B R 7onst path5 . . q = require_ _ [ |(\'path\');
module.exports = {
//...
module: {
&nd k % ; s p ^ Absp; rules: [
&U L 0 + }nbsp; {
&nbs: Z U L K =p; test: /\\) G g d ;.7 l u # *js[x]?$/,
[ & U _ &n@ V S N - s )bsp; use: [\S y r I 3 _ C'babel-loader\'],
&nF D a ] - d $ k 0bsp; | M = 5  U C P;&5 F 7 + h a ~ snbsp; b M x / M X +  D % * f C |; inclu: u V n c ^ 1de: [path.resolve(__dirname, \'src\')]
&nu m s , b Dbsx S ] t L } t | tp; }
]
},
}
下图是) q i [ * z r G r我未配置 include 和配置了 include 的构建结果对比:
2. cache-loader
在一些性能开销较大的 loader 之$ V * E V j h前添加 cache-loader,将结果缓存中磁盘中。默认保存在 node_mo: .dueles/.cache/cache-loader 目录下。
首先K & i 8 { f r o安装依赖:
npm install cache-loader -D
cache-loader 的配置很简单,放t W + $ k ] t在其他 loader! k j 6 J s 1 ; ^ 之前即可。修改Webpack 的配置如下:
module.exports = {] 7 m L
//...
module:&nbs/ % E $ } @ & Ap;{
&nh % b 4 0 9 -bsp;  d r 3 X I; //我的项目中,babel-loader耗时比较长,所p u !以我给它配置了`cache-loader`
rules: [
&ne m c 1bsp; I M L 8 ^ {
&nb4 + Fsp; test: b 1 K Q | a ; ,;/\\.jsx?$/,
use: [x o f o\e ) [ 9 %'cache-loader\',\'babel-loader\']
}
]
&nbs[ Z S k U Wp; }
}
如果你跟我一样,只打算给 babel-loader 配置 c2 a ) K U nache 的话,也可以不使用 cache-loader,给 babel-loader 增加选项 cacheDi@ k 4 Nrectory。
cacheDirectory:默| e q 2 T Q T Z #认值为 false。当有设置时,指定的目录将用来缓存 loader 的执行结果。之后的 Webpack 构建,将会尝试读R v P 3 { -取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 重新编译过程。{ t V = ~ Y , t设置空值或者 true 的话,使用默认缓存目录:node_modules/.cache/babel-loader。开启 babel-loader的缓存和配置 cache-loader,我比对了下,构建时间很O [ Y s : S @ %接近。
3.happypack
由于有大量文件@ I ;需要解析和处理,构建是文件读写和计算密集型的操作,特别t i T . h } k [ o是当文件数量变多后,Webpack 构建慢的问题会显得严重。文件& j - = f _ R读写和计算操作是无法避免的,那能不能让 Webpack 同一时刻处理多个任务,发挥多核 CPU 电脑的威力,以提升构建速度r ^ O $呢?
HapF X 4 j 1pyPack 就能让 Webpack 做到这点,它把任务分解给多个子进程去并发的执行,子进程处理完后再把结果发送给主进程。
首先需要安装 happypack:
npm install happypack&nb& i #sp;-D
修改配Z ) p U K T D置文件:
const Happypack = require(\'happypack\');
module.eX b g Lxports = {
//...
&nbs $ 4 & u D 3p; module: {
rules: Z F 7 G M 8[
&Z * tnbsp; {
 6 ~ w A , P V A A; test} p - G: /\\.js[x]?! - V 3 i s #$/,
` $ / &nbs@ + , 5 u & :p; use: \'HappypackX e { # ] X k v h/load0 P -er?id=js\',
&nM _ } db~ j l p qsp; include: [path.resolve(__dirname, d & ;\'src\'R j W I O)]
&nbsS T { E zp; },
&nbsn + 5 bp; {
 D n L; &, / V P 7nbsp; 6 ; e | +; test: /\\.cf M e B 6 M E 7 #ss$/,N & c Q F !
&n9 6 0 , ! / fbsp; use: \'Happypack/loader?id=css\',
&nbsH z _ h d / /p; z 2 v ! x F / { 8 include: [
&nbB 4 O ?sp;  g I 4 M } s Q } {; path.resolve(__dirname, \'src\Y v d ? C J'),
 } C e { x L , ?;  a [ ] e }; path.resolve(__dirname, \'node_modules\',&nb2 C | ] ~ 1 | % wsp;\'bootstrap\', \'dist\')
U L k g y ]
}
0 C C $ $ &n1 V : H R 4 Zbsp; ]
&nJ Z f Z bsp; X 7 G - },
&1 . i znbsp; plugins: [
v = h ] _ ; N |&nbsJ + Fp; new Happypac~ Z Q qk({
[ P P ! [ g id: \'js\', //y ~ % w { S E和rule中的id=js对应
&nbsJ g mp; g P q Y l; //将之前 rule 中的 loader&nbw { 8 5 j 8 S ? ksp;在此配置
&, / /nbsp; use: [\'babel-loader\'] //必须是数组
&nbsb O 9 9 _ s [ j ^p; &n? L ? s *bsp; }),
&a J x .nbsp; S N 5 J s # - r % J . S W 8 `; new Happypac# B ` 3 b 7 :k({
 ( h I : # [; &b 0 E ; ) Q tnbsp; id: \'css\',//和rule中i ) / } v Q G L的id=css对应
&n) t m ?bsp;  J ! N [ H / X; use: [\'style-loader\', \'c) O 9 9 o T : = oss-loader\',\'postcss-loader\'],
})
]
}
happypack 默认开启 CPU核数 - 1 个进程,当然,我们也可以传递 threads 给 Happypack。
说明:当 postcss-loader 配置在 HD ^ / 0 ] xappypack 中,必须S I P 7 `要在项目中创建 postcss.config.js。
//postcss.config.js
module.exports } O n T =;= {
plugins: [
&I Q ^ _ 4 ~ 5 ^nbsp; require(\'autopp c % ; F ( 4 % %refixer\')()
&nb| l Z % a , p Vsp; &nb{ x @ U P {sp;]
}
否则,会抛出错误: Error: No PostCSS Config found
另外,当你的项目不是很复杂时,不需要配置 happypack,因为进程的分配和管理也需要时间,并不能有效提升构建速度,甚至会变慢。
4.thread-loader
除了使用 Happypack 外,我们也可以使用 thread-loader ,把 thD : K (read-loader 放置在其它 loader 之前,那么放置在这个 loader 之后的 loader 就会在一个单独的 worker 池中运行。
在 worker 池(worker pool)中运行的 loader 是受到限制的。例如:
- 这些 loader 不能产生新的文件。
- 这些 loader 不能使用定制的 loQ E 4ader API(也就是说,通过插件)。
- 这些 load+ Y x P 1 N -er 无法获取 webpack 的选E ( , c 5 P v E项设置。
首先安装依赖:
npm&nbq s o r $sp;install thread-loader -D
修改配置:
module.exports = {
&nb] 2 G v & g p psp; module: {
//我的项目中,babel-loader耗时比较长,所以我给它配置 thread-ly p O K % z Poader
&nf , 9 zbsp; rq r ^ S X a 0ules: [
&nbk 3 a T ksp;  7 5 ! ];{
{ u N `  1 ( _ k { O - n; Z S ~ . s [ T &nB L B Mbsp;test: /\\.jsx?$/,
&nbj ? J 4 u nse V L j 1 ` 5p; &n& C d K p 5bsp; &nC 1 c w E 4 Gbsp;u : z x O L use:&nb= E $ 1 $ 4 msp;[b H 6 / o V 8 C\'thread-loader\', \'cache-loaderb y i = 9 ( * g n\', \'babel-loader\']
&/ a y C b p ~ A 4nq h g nbsp;&nR S Y f g pbs| @ O 5p;&ng G Absp; }
&nbsv / A _ G x , 2p; ]
 0 - Z 1 ?; }
}/ V E p (
thream G h 9 1 td-loader 和 Happypack 我对比了一下,构建时间基本没什么差G . Y X n J * . :别。不过 thread-loader 配置起来为简单。
5.开启 JSJ ] % l z ` 多进程压缩
虽然很多 webpack 优化的文章上T / r Y会提及多进程y t S q压缩的优化,不管是 webpack-parallel-uglify-plugin 或者是 uglifyjs-webpack-plugin 配置 parallel。不过这里我要说一句,没必q + h ; ~ n要单独安装这些插件,它们并不会让你的 Webpa9 f Q K e a a [ck 构建速度提升。
当前 Webpack 默认使用的是 TerserWebpackPlugin,默认就开启了多进程和缓存,构建时,你的项目中可以看到 ters+ G 1 k Ier 的缓存文件 node_modules/.cache/terser-webpac` D { U s J a *k-plugin。
6.HardSourceWebpackPlugin
HardSourceN ] I dWebpackPlugiI 1 G K 1n 为模块提供中间缓存,缓存默认的存放路径是: node_modules/.cache/hard-source。
配置 hard-source-webpack-plugin,首次构建时间没有太大变化E w o 8 +,但是第二次开始,构建时间大约可以节约d 2 x N 0 ( q Z { 80%。
首先安装依赖:
npm& 6 i _ (nbsp;install hard-source-webpack-plugin -D
修改 webpack 的配I { + c置:
//webpack.config.js
var HardSourceWebpackPlugin = require(\'hard-source-webpav ` m { $ Dck-plugin\');
module.exports S O @ x %= ) y b g M O{
q c C $ # M //...
plugins: [
&nbse @ # 8 c c ? * rp; new HardSourceW` ! [ d | w xebpackPlugin()
]
}
用另外一个比较大的项目测试了e [ L下,配置了 HardSourceWebpackPlugin,构建时间从 8S 左右降到了P V - 2S 左右。
HardSourceWebpv ) h R [ & $ackPlugin文档中 列出了一些你可能会遇到的问题以及如何解决,例如热更新失效,或者某些配置不生效等。
7.noParse
如果一些第三方模块没有AMD/CommonJS规范版本,可以使用 noParse 来标识这个模块,这样 Webpack 会引入这些模块,但是不进行转化和l a P s e | l e解析,从而提升 Webpack 的构建性能 ,例如:jquery 、lodash。
noParse 属性的值是一个正则表达式或者是一个 function。
//webpacE k T z = @ y r =k.config.js
module.exports = {
//...
module: {
&nbsd H xp; noParse: /jquery|lodash/
&nbI u . E 3 _ g h ,sp;}
}
我当前的 webpack-optimize 项目中,没有使用 jqd : 6 $ l M muery 或者是 lodash。
因此新建一个项目测m D m O ; & Y试,只引入 jquery 和 loadsh,然后配置 noParse 和不配置 noParse,分别构建比对时间。
配置noParse 前,构建需要 2392ms。配置了 noParse 之后Z n N ( } ] / M,构建需要 1613ms。如果你使用到了不需要解析的第三方依赖,那么配置 noPR n x # warse 很显然是一定会起到优化作用的。
8.resolve
resolve 配置 webpack 如何寻找模块所对应的文件。假设我们确定模块都从根目录下的 node_moF Z G - ? Z o ,dules 中查找,我们可以配置:
//webpack.config.js
const&nbs{ D K . zp;path = require(\'path\');
module.exports = {
//...
reC W u 1 g ! O O gsolve: {
&i { ] / = $ 4 nbsp; ( :; modules: [path.resolve(__dirname, \'node_modules\')],
&w 5 C 7 P $ q Qnbsp; }
}
需要记住的是] v m u Z,如果你配置了上述的{ V r R w @ ! 7 J resolve.moudles ,可能会出现问题,例如,你的依赖中还存在m Q 4 { % node_modulesV + M d C 目录,那么就会出现,对应的文件明明在,但是却提示找不到。因此呢,个人不推荐配置这个。如果其他同事不熟悉这个配置,遇到这个问题时,会摸不着头脑。
另外,resolve 的 extensions 配置,默认是 [\'.js\', \'.json\'],如果你要对它进行配置,记住将频率最高的后缀放在第一位,并且控制列表的长度,以减少尝试次数。
本f ( : X X G f *项目较小,因此测试时,此处优化效果不明显。
9.IgnorePlugin
webpack 的内置插件,作用是忽略第三方包指定目录。
例如: moment (2.24.0版本) 会将所有本地化内容和核心功能一起打包,我们就可以使用 Igi s C t ] /norePlugin 在打包时忽略本地化内容。
//webpack.config.js
module.z W l . [ 5 / / ,exports =&q : jnbsp;{
//...
plugins: [
//忽e D - x n略&nR 3 K R r [ P 5bsp;moment 下的 ./locale 目录
&nb( F y . H T w 5 ksp; Q T ) U . B new webpack.IF k g R ; [gnoreP+ + ~lugin(/^\\.\\/lo1 G ~cale$/, /moment$/)
]
}
在使用的时候K : X 5 f Y,如果我们需要指定语言,那么需要我们手动的去引入语言包,例如,引入中文语言包:
imp+ ! s 3 R (ort moment f y { ` 5 1 * } Irom * m P 9;\'moment\';
import \'moment/locale/zh-cn\';2 = e 1 ` o X t %// 手动引入
index.js 中只引入 moment,打包出来的 bundle.js 大小为 263KB,如果配置了 IgnorePlugin,单独引入 moment/locale/z} R B f U k l rh-cn,构建出来的包大小为 55KB。
10.externau w % [ D Mls
我们可以将一些JS文件存D p f R P储在 CDN 上(减少 Webpack打包出来的 js 体积)N U | & 5 ],在 index.html 中通过 <script> 标签引入,如:
<!DOCTYPE html>
<html lang=d g $ k R\"en\@ W } V }">
<head>
<meta charset=\"UTF-8\">
<meta name=\"viet C L n q G $ ,wport\" content=\"width=device-width,: ^ Y X ( g P initial-scale=1j c a Z ? # (.0\">
<meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">
&- o , ^nbsp; <title>Document<J 7 b x Q;/title>
</head>
<body>
 2 w a 6; &nR 7 ) r z T p P Rbsp;<div&n{ k d } o Cbsp;id=\"B j Y S S %root\">root</div>
&i - h tnbsp; <script src=\"http://libs.baidu.com/jquery/2.0.0/jquery.min.js\"></script>
</body>
</html>
我们希望在使用时,仍然可以通过 import 的方式去引用(如, ) f ~ z o b ; import $ froh Y / M & i [ {m \'jquery\'),并且希望 webpack 不会对其进行打包,此时就可以配置 externals。
//webpack.config.js
module.exports = {
&X u j [ [ 2nbsp;//...
 V j `; 4 F N B externalsF ) s b: {
&nb] $ w { / j k f ?sp;&nb@ , zsp; T & } E ~ m h //jquery通过script引入之后,全局中即有了 jQuery 变量
9 = x j &n4 ( S q Mbsp;\'jquery\': \'jQuern h by\'
4 p h }
}
11.DllPlugin
有些时候,如果所有的JS文件都打成一个J7 ` % @ r / H kS文件,x { ) x & w会导致最终生成的JS文件很大,这个时候,我们就要考虑拆分 bundlt $ *esK V 2 $ [ e d。
DllPlugin 和 DLLReferencePlugin 可以实现拆分 bundles,并且可以大大提升构建速度,DllPlugin 和 DLLRef^ I M r ~erencePlugin 都是m f R Q B B G T webpack 的内置模块。
我们使用 DllPlugin 将不会频繁更新的h N O库进行编译,当这些依赖的版本没有变化时,就不需要& & a p % T重新编译。我们新建一个 webpack 的配置文件,来专门用于编译动态链u C t N 3 q S接库,例如名为: webpack.config.dll.js,这里我们将 react 和 react-dom 单独打包成一个动态链接库` b G e X t J n q。
//webpack.config.dll.js
const webpack = require(\'webpack\');
constp @ path&( y g E K 3 )nbsp;= require(\'path\');
m( W { oodule.exports = {
entry: {
&nbs` 7 1 M u & r Lp; &nn ` ] pbsp;react: [\'react\',&n_ 0 w = U bsp;\'rP d H - D 4eact-dom\']
},
&d S znbsp; &nbsX G . * V x Vp;mode: \'prodX m ` [uction+ Y c A j ^\',
= q | V : output: {
&nb4 k ) 7 Wsp; &nbJ ^ * i a D % Xsp; &nbs} ( 8 z C P V 5 ,p;filA ) R O h P bename: \'[name].dll.[h + g ) & T b N )ash:6].js\',
path: path.resolve(__dirname, \'dist\', \'dll\'),
X d { ~ j ? 4 X B ` C ~ h; libraa k 0 f F | O zry: \'[name]_dll\' //暴露给外部使用
//libraryTar8 a V v i nget M 2 C , D指定如何暴露内容,缺省时就是&T H P c Wnbsp;var
9 4 | $ ! C },
plugins: [
 ! @ 8; new webpack.DllPlugin({
&n; y _ , qbsp; &nbst ? / _p? r t e u O M; //name和library一 N i q k i .致
l J r F W ^ L 3 j &# 0 unbsp; &nb? 0 . 2 o qsp;name: \'[name]_dT i ? F 3 Dll\',
path: path= R & ( @ _ S B.resolve(__dirn6 _ 9 | ) I A R rame, \| J @ ^ , /'dist\', \'dll\', \'manifest.json\') //manifest.json的生成路径
&L L ] _ Y ~nbsp; })
&n+ H j xbsp; ]
}
在 package.json 的 scripts 中增加:
{
O M * D a\"scripts\": {
&nbs~ C % 2 = r { &p; &nbs` ] k y m @ V hp; &nbs9 = U # T 7 * vp;\"dev\": \"NODE_ENV=dM L _ u o O U =evr A P R L @ jelopment webpack-dev-server\",
&, 2 @ lnbsp; &d [ !nbsN N j @ op; \"build\": \"NON - $ s 8 e x n [DE_ENV=production webpack\",
&n* I F l t [ 0 VbsT & 2 : k [ 9 R 9p; \"build:dll\": } O T n j /;\"webpack d } L y v z % N--config webpack.config.dll.js\N V m - +"
},
}
执行 npm run build:all,可以看到 dist 目录如下,之所以将动态链接库单独放在 dll 目录下,主要是为了使用 CleanW n j 4 $WebpackPlugin 更为方便的过滤掉动态链接库。
dist
└── dll
= W $ l 5 [ ) y├── manifest.json
└── react.dll.9dcd9d.js
manifK f Qest.json 用于让 DLLReE O 1 f ? 5 G (ferencePlugin 映射到相关依赖上。
修改 webpack 的主配置文件: webpack.config.js 的配置:
//webpack.config.j` y O . a |s
const webpack = reF 1 9 fquire(\'webpack{ l u 7 6 ,\');7 E v 0 i o (
const pa* E t # . k ^th&nby : ` / 6 z bsp;= require(\'path\');
module.eO i $ z K )xports = {
//...
devServer: {
contentBase: path.resolve(__dirname, \'dist\')
},
plugins: [
new w5 o T : ~ } 5ebpack.DllReferencePlugin({
 6 8 6; &/ c g S N 1 J 6nbspH 3 w Q [ d + g z; manifest:&nk Y z tbsp;path.resolve(__dirname, \'dist\', \'dll\', \'manifesta X z y.json\')
}),
&nbg 0 9 o @ ; .sp; new CleanWebpackPlugin({
&n} o P Ebsp; &n= P w c hbsp; cleanOnceBeforeBuildPa; R G D =tterns: [\'**/*\', \'!dllO @ O ] = Y R &\', \'!dll/**\'] V ; 4 x K//不删除dll目录
&nbF m / + , J & Psp; }),
 U , ^ ) k |;&n! ( @ {bsp; //...
]
}
使用 npm run build 构建,可以看到 bundle.js 的体积大大减少。
修改 public/index.html 文件,在其中引入 react.dll.js
<script src=\"/dll/react.dll.9dcd9d.js\"></script>N { n 2 L
构建速度
包e x / x { W U E m体积
dll-size.jpeg
12B 3 c I.抽离公共代码
抽离公共代码是对于多页应用来说的,k * ] F s ) [ - t如果多个页面引入了一些公共模块,Z j 6 h Y ! Q 7那么可以把这些公i E ` e 共的模块抽离出来,单独打包。公M i p M 8 h s共代码只需要下载一次就缓存起来了,避免了重复下载。
抽离公共代码对于单页应用和多页应该在配置上没有什么区别,都是配置在 optimization.splitChuP : jnks 中。
//webpack0 u r.config.js
module.exports0 % @ M ; J ] s t = {
optimization: {
&n6 w I s k Mbsp; 0 z 1 E l; splitChunks: {//分割代码块
 N Q X ] E i r I; &n# ! @ 5 } }bsp; = k z 2 M cacheGroups: {
 { @ P; &nbh U % f ~ 9 Wsp# . ] W E 3 d `; vendor: {
O C R / z - g &n5 r 3 0 ] : { xbsp; //! b x v第三方依赖
&n[ * % a I i U k Sbsp; &nbsI S }p; q m C &nbsX X p; &W m 2 ` A ( Pnbsp;priority: 1, //设置优* 0 l先级,首先抽离第三方模块1 n r ,
&n? p d - N [ s Dbsp; L f = B & &n. D n Rbsp; name: \'vendor\',
&nB f + w a _ %bsp; &nY j J R - 5bsp; 1 & [ p; &nS o ; n [ # c dbsp; test: /node_modv _ 2 * k d S ;ules/,
&nbsN 7 Q K $ { w }p; q b 0 F i 8 m h ~ 0 ; U;&np B Y c S U 4 Pbsp; &nl C t s T z kbspS H d P t;chunks: \'initial\',
&K _ E 1 4 j 7 knbsp; &~ , . : d !nbsp; minSize: 0,
&nb, C R Jsp;  ? = { % g E; &nbd V i v zsp; minChunks:&n( u 1 t o | : bbsp;1 //最少引入了1次
},
 m c ^; : _ N w c H K //缓存组
M U M 7 Q v &n] F 8 jbsp; common: {
&nw [ _ J kbsp; //公共模块
chunks: \'initial\',
y r ] 0 m ? Z * (  1 ^ ( 5 ^ - | &; &ni V $ u q h *bZ 7 c /sp; name: \'common\',
 w / d o & | 7;  t @ ~ ; f E;  F T Q x i K ] 4 [; minSize: 100, //大小超过100个字节
 Z ; I v + u n v s; &I W } ^nbsp;&f / )nbsp; minChunks: 3 //最少引入了3次
&n% ! Rbspv ) $ K S W h; Q 3 @ 3 # - v 8 &j ` r !nbsp; }
 6 2 n; &nbQ R [sp; }
 i ^ @ E 1 9; }
}
}
即使是单页应用,同样可以使用这个配置8 x 9 * + V & 9 l,例如,打包出来的 bundle.js 体积过大,我们可以将一些依赖打包成动态链接库,然后将剩下的第三方依赖拆出来。这样可以有效减小 bundle.js 的体积大小。当然,你还可以继续提取业务代码的公共模块,此处,因为我项目中源码较少,所b p Y 0 { :以没有配置l a x z M { ~ n 1。
splitChunks.jpeg
runtimeChunk
runtimeChunk 的作用是将包含 chunk 映i L - _ 7 d射关系的列表从 main.js 中抽离出来,在配置了 s= * U )plitChunk 时,记得配置 runtimeChunk.
module.U & , : |exports = {
&nbsc @ X | y M pp; //...
optimization: {
&nbV K . Psp; runtimeChunT I Qk: {
&nk g M ? F b p ~bsp; naj + Jme: \'manifest\'
2 U P e | O V b }
}
}
最终构建出来的文件中会生成一个 manifest.js。
借助 webpack-bundle-analyzer 进一步优化
在做 webpack 构建优化的时候,vendor 打出来超过了1M,react 和 react-dom 已经打包成了DLL。
因此需要借助 webpack-bundle-analyzer 查看一下是哪些包的体积较大。
首先安装依赖:
npm install webpack-bundle-analyzer -D
使用也很简单,修改下我们的配置:
//webpack.config.prod.js
const BundleAI k F t ) ;nalyzerPlugin = require(\Q 9 P'webpack-bundle-analyzer\').BundleAnalyzerPlugin;
const&x pnbsp;merge = require(\'webpack-merge\');
const baseWebpackConfig = require(\'./? n r 0 Jwebpack.config.c 4 t 8 ` Z j $ }base\');
module.exports = merge(baseWebpackConfig, {
&nbs^ V { ; - Lp; //....
j A 8 plugins: [
 5 ? F y; //...
new BundleAnalyzerPlugin(),
]
})
npm run build 构建,会默认打开:http://127.0.0.1:8888/,可以看到各个包的体积:
进一步对 vendor 进行拆分,将 veQ % ( N cndor 拆分成了4个(使用 splitChunks 进行拆分即可)。
module.exports = {
&nbsD f O T y C 0 I zp; optimic _ G B e ^ ) ization: {
concatenateModules: false,
sW V L } ) m jplitChunks: {//分割代码块
 , H @ | u K 7 J; &nb/ ` / B / ] i k 8sph a 0 D T Z A y; maxInitia? J _ S 2 ^ LlRequests:6, //默认是5
&V U jnbsp; cacheGrc | . Z q Doups: {
vendor: {
 Y b w p;  ( u n e X 2 |; //第三方依赖
&n_ i cbsp; s $ D & priority: 1H y ; j U ? c,
&nb5 x _ 7 {sp; name: \'vendor\',
test: /node_moduq w c oles/,
 g l V /; chunks:&nU ~ K ` E 6 - [bsp;\'initial\p { y',
minSize:&nbsc 7 Q n y 6 Y *p;100,
&nP _ Y q Y ^bsp; # r R ! O r minChunks: 1 //重复引入了几次
},
 S 5 ! P = k; \'lottie-web\': {
&n 4 s ] Q x 8 ^ Ubsp; &no - { q j C =bsp;name: \"lottie[ { & = q-web\", // 单独将&nb= G $ U 7sp;react-lottie 拆包
y B G fpriority: 5, // 权重需大于`vendor`
test: /[\\/]nodek M [ U _ A U m_modules[\\/]lottie-web[\\/]/,
 n ! @ d W N [ ? a; ch# ! iunks: \'initial\; O b Z m 7 Z 7 J',
 % = y 7 ) N F b;minSize: 100,
&nbI J Z Ksp; &C v 0 V L J + h Znbsp; minChunks: 1 //重复引入了几/ T K K E A X次
&k Q a { v 6 U c qnbsp; &$ V cnbsp;},
&V l d _nbsp; //...
}
&f ~ D k ]nU G #bsp; },
},
}
重新构建,结果如下所示:
13.webpa+ U # 2 N ? . 6cb - R } ?k自身的优化
tree-shaking
如果使用ES6的import 语法,那么在生产环境下,会自动移除没有使用到的代码。
//math.js
const add&_ 8 B E p , j @ Lnbsp;= (a, b) =>&nm ` r a H l r Xbsp;{
&nbsS J q l +p;console.log(\'aaaaaa\')2 g z 4 ^ + =
&n+ [ W $ H d Hbsp; return . f j;a + b;A + c { & T / u b
}
const minus = (a, b) =&m F ; .gt; F D n V $ | / g u{
console.log(\'bbbbbb\')
return a&nbk 6 0 B U d zsp;- b;
}
export&nb# w 3 ^ F O 1 Hs, h u f . , sp$ L ) J C 3 r @ ?;{
add,
u 9 z 7 X E minus
}
//index.js
import {add, minus} from \'./math\';
add(2,3)! P *;
构建的最终代码里,minus 函数不会被打包进去。
scope hosting 作用域提升
变量提升,可以减少一些变量声明。在生产环境下,默认开启。
另外,大家测试的时候注意一下,speed-measure-webpack-plugin 和 HotModuleReplacementPlugin 不能同时使用y ~ 7 2 %,否则会V x K a报错:
babel 配置的优化
如果你对 babel 还不太熟悉的话,那么可以阅读这篇文章:不容错过的 Babeln ^ k [ v7 知识。
在不配置 @babel/plugin-transform-runtime 时a p R E,babel 会使用很小的辅助函数来实现类似 _createClass 等公共方法。默认情况下,它将被注入(inject)到需要它的每个文件中。但是% j h =这样的结果就是导致构建出来的JS体积变大。
我们也并不需要在每个 js 中注入辅助函数,因此我们可以使用 @babelv # S # ) U/plugin-transform-runtime,@babel/plugin-t1 r N O :ransformH / W f [-runtime 是一个可以重复使用 Babel 注入的帮助程序,以节省代码大小的插件。
因此我们可以在 .babelrc 中增加 @babel/plugin-transform-runtime 的A Y | H )配置。
{
\Z d ? | 5 P / e _"presets\": [],
\b Y r d 3"plugins\"A y 8 k _: [
[
&n] , m Hbsp;\"@babel/plugin; = ; r o d ] O-transform-runtime\"
O 9 1 S ~ w&nbs ` a J Ysp; &nbs8 t & { ! 7 [ jp;]
&nbsb i + o )p; ]e l G [ 6 U $ w #
}
以上就是我d l C目前为止使用到的一些优化,如果你有更好的优化方式,欢迎在评论区留言,感谢阅读。
参考文档z K ~ ^:
- webpack优化的一些基本方法
- 模块(Module)
- IgnorePlugin
- DllPlugin
- 使用 Webpack 的 DllPlugin 提升项目构建速度
- 使用 HappyPack
- webax d I E & vpck4抽取公共模块“SplitChunksPlugin”
- webpack之优化篇(四):hard-source-webpack-plugin,webpack DllPlugin配置的代替方案
— 完 —