浅谈Webpack4 Tree Shaking 终极优化指南(4)

这实际上是件好事。如果 Jest 需要重新编译所有库的话,将会大大增加测试处理时间。然而,虽然我们不想让它重新编译所有代码,但我们希望它重新编译使用 es2015 模块的库,这样才能在单元测试里使用。

幸好,Jest 在它的配置中为我们提供了解决方案。我想说,这部分确实让我想了很久,并且我感觉没必要搞得这么复杂,但这是我能想到的唯一解决方案。

配置 Jest 重新编译库代码

// 重新编译库代码的 Jest 配置 const path = require('path'); const librariesToRecompile = [ 'Library1', 'Library2' ].join('|'); const config = { transformIgnorePatterns: [ `[\\\/]node_modules[\\\/](?!(${librariesToRecompile})).*$` ], transform: { '^.+\.jsx?$': path.resolve(__dirname, 'transformer.js') } };

以上配置是 Jest 重新编译你的库所需要的。有两个主要部分,我会一一解释。

transformIgnorePatterns 是 Jest 配置的一个功能,它是一个正则字符串数组。任何匹配这些正则表达式的代码,都不会被 babel-jest 重新编译。默认是一个字符串“node_modules”。这就是为什么Jest 不会重新编译任何库代码。

当我们提供了自定义配置,就是告诉 Jest 重新编译的时候如何忽略代码。也就是为什么你刚才看到的变态的正则表达式有一个负向先行断言在里面,目的是为了匹配除了库以外的所有代码。换句话说,我们告诉 Jest 忽略 node_modules 中除了指定库之外的所有代码。

这又一次证明了 JavaScript 配置比 JSON 配置要好,因为我可以轻松地通过字符串操作,往正则表达式里插入库名字的数组拼接。

第二个是 transform 配置,他指向一个自定义的 babel-jest 转换器。我不敢100%确定这个是必须的,但我还是加上了。设置它用于在重新编译所有代码时加载我们的 Babel 配置。

// Babel-Jest 转换器 const babelJest = require('babel-jest'); const path = require('path'); const cwd = process.cwd(); const babelConfig = require(path.resolve(cwd, 'babel.config')); module.exports = babelJest.createTransformer(babelConfig);

这些都配置好后,你在测试代码应该又能跑了。记住了,任何使用库的 es2015 模块都需要这样配置,不然测试代码跑不动。

Npm/Yarn Link 就是魔鬼

接下来轮到另一个痛点了:链接库。使用 npm/yarn 链接的过程就是创建一个指向本地项目目录的符号链接。结果表明,Babel 在重新编译通过这种方式链接的库时,会抛出很多错误。我之所以花了这么长时间才弄清楚 Jest 这档子事儿,原因之一就是我一直通过这种方式链接我的库,出现了一堆错误。

解决办法就是:不要使用 npm/yarn link。用类似 “yalc” 这样的工具,它可以连接本地项目,同时能模拟正常的 npm 安装过程。它不但没有 Babel 重编译的问题,还能更好地处理传递性依赖。

针对特定库的优化。

如果完成了以上所有步骤,你的应用基本上实现了比较健壮的 tree shaking。不过,为了进一步减少文件包大小,你还可以做一些事情。我会列举一些特定库的优化方法,但这绝对不是全部。它尤其能为我们提供灵感,做出一些更酷的事情。

MomentJS 是出了名的大体积库。幸好它可以剔除多语言包来减少体积。在下面的代码示例中,我排除了 momentjs 所有的多语言包,只保留了基本部分,体积明显小了很多。

// 用 IgnorePlugin 移除多语言包 const { IgnorePlugin } from 'webpack'; const config = { plugins: [ new IgnorePlugin(/^\.\/locale$/, /moment/) ] };

Moment-Timezone 是 MomentJS 的老表,也是个大块头。它的体积基本上是一个带有时区信息的超大 JSON 文件导致的。我发现只要保留本世纪的年份数据,就可以将体积缩小90%。这种情况需要用到一个特殊的 Webpack 插件。

// MomentTimezone Webpack Plugin const MomentTimezoneDataPlugin = require('moment-timezone-data-webpack-plugin'); const config = { plugins: [ new MomentTimezoneDataPlugin({ startYear: 2018, endYear: 2100 }) ] };

Lodash 是另一个导致文件包膨胀的大块头。幸好有一个替代包 Lodash-es,它被编译成 es2015 模块,并带有 sideEffects 标志。用它替换 Lodash 可以进一步缩减包的大小。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/9223838595148c24edd06e3c33baaa41.html