代码优化到⽣成产物阶段的效率提升,这⼀阶段的优化可以分为两个不同的⽅向:
- 针对某些任务,使⽤效率更⾼的⼯具或配置项,从⽽提升当前任务的⼯作效率。
- 提升特定任务的优化效果,以减少传递给下⼀任务的数据量,从⽽提升后续环节的⼯作效率。
以提升当前任务⼯作效率为⽬标的⽅案
⼀般在项⽬的优化阶段,主要耗时的任务有两个:⼀个是⽣成 ChunkAssets,即根据 Chunk 信息⽣成 Chunk 的产物代码;另⼀个是优化 Assets,即压缩 Chunk 产物代码。
-
⾯向 JS 的压缩⼯具
Webpack 4 中内置了 TerserWebpackPlugin 作为默认的 JS 压缩⼯具,之前的版本则需要在项⽬配置中单独引⼊,早期主要使⽤的是 UglifyJSWebpackPlugin。这两个 Webpack 插件内部的压缩功能分别基于 Terser 和 UglifyJS。在作为 Webpack 插件的 TerserWebpackPlugin 中,对执⾏效率产⽣影响的配置主要分为 3 个⽅⾯:
- Cache 选项:默认开启,使⽤缓存能够极⼤程度上提升再次构建时的⼯作效率。
- Parallel 选项:默认开启,并发选项在⼤多数情况下能够提升该插件的⼯作效率,但具体提升的程度则因项⽬⽽异。在⼩型项⽬中,多进程通信的额外消耗可能会抵消其带来的益处。
-
terserOptions 选项:即 Terser ⼯具中的 minify 选项集合。这些选项是对具体压缩处理过程产⽣影响的配置项。
a. compress 参数的作⽤是执⾏特定的压缩策略,例如省略变量赋值的语句,从⽽将变量的值直接替换到引⼊变量的位置上,减⼩代码体积。⽽当 compress 参数为 false 时,这类压缩策略不再⽣效。
b. mangle 参数的作⽤是对源代码中的变量与函数名称进⾏压缩。
c. 当 compress 参数为 false 时,压缩阶段的效率有明显提升,同时对压缩的质量影响较⼩。在需要对压缩阶段的效率进⾏优化的情况下,可以优先选择设置该参数。
-
⾯向 CSS 的压缩⼯具
- OptimizeCSSAssetsPlugin(在 Create-React-App 中使⽤)
- OptimizeCSSNanoPlugin(在 VUE-CLI 中使⽤)
- CSSMinimizerWebpackPlugin(2020 年 Webpack 社区新发布的 CSS 压缩插件)。
这三个插件在压缩 CSS 代码功能⽅⾯,都默认基于 cssnano 实现,因此在压缩质量⽅⾯没有什么差别。
在压缩效率⽅⾯,⾸先值得⼀提的是最新发布的 CSSMinimizerWebpackPlugin,它⽀持缓存和多进程,这是另外两个⼯具不具备的。
以提升后续环节⼯作效率为⽬标的⽅案
-
Split Chunks(分包)
指在 Chunk ⽣成之后,将原先以⼊⼝点来划分的 Chunks 根据⼀定的规则(例如异步引⼊或分离公共依赖等原则),分离出⼦ Chunk 的过程。Webpack 4 中内置的 SplitChunksPlugin,该插件在 production 模式下默认启⽤。其默认的分包规则为 chunks: 'async',作⽤是分离动态引⼊的模块 (import('...')),在处理动态引⼊的模块时能够⾃动分离其中的公共依赖。设置为 chunks: 'all',则能够将所有的依赖情况都进⾏分包处理,从⽽减少了重复引⼊相同模块代码的情况。
SplitChunksPlugin 的⼯作阶段是在 optimizeChunks 阶段(Webpack 4 中是在 optimizeChunksAdvanced,在 Webpack 5 中去掉了 basic 和 advanced,合并为 optimizeChunks),⽽压缩代码是在 optimizeChunkAssets 阶段,从⽽起到提升后续环节⼯作效率的作⽤。
-
Tree Shaking(摇树)
指在构建打包过程中,移除那些引⼊但未被使⽤的⽆效代码(Deadcode elimination)。这种优化⼿段最早应⽤于在 Rollup ⼯具中,⽽在 Webpack 2 之后的版本中, Webpack 开始内置这⼀功能。
引⼊不同的依赖包(lodash vs lodash-es)、不同的引⼊⽅式,以及是否使⽤ babel 等,都会对 Tree Shaking 的效果产⽣影响-
ES6 模块: ⾸先,只有 ES6 类型的模块才能进⾏ Tree Shaking。因为 ES6 模块的依赖关系是确定的,因此可以进⾏不依赖运⾏时的静态分析。⽽ CommonJS 类型的模块则不能,需要依赖第三⽅提供的插件(例如 babel-plugin-lodash 等)才能实现动态删除⽆效代码。
-
引⼊⽅式:以 default ⽅式引⼊的模块,⽆法被 Tree Shaking;⽽引⼊单个导出对象的⽅式,⽆论是使⽤ import * as xxx 的语法,还是 import { xxx } 的语法,都可以进⾏ Tree Shaking。
-
sideEffects:在 Webpack 4 中,会根据依赖模块 package.json 中的 sideEffects 属性来确认对应的依赖包代码是否会产⽣副作⽤。只有 sideEffects 为 false 的依赖包(或不在 sideEffects 对应数组中的⽂件),才可以实现安全移除未使⽤代码的功能。
-
Babel:在 Babel 7 之前的 babel-preset-env 中,modules 的默认选项为 'commonjs',因此在使⽤ babel 处理模块时,即使模块本身是 ES6 ⻛格的,也会在转换过程中,因为被转换⽽导致⽆法在后续优化阶段应⽤ Tree Shaking。⽽在 Babel 7 之后的 @babel/preset-env 中,modules 选项默认为 ‘auto’,它的含义是对 ES6 ⻛格的模块不做转换(等同于 modules: false),⽽将其他类型的模块默认转换为 CommonJS ⻛格。
-
网友评论