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

默认情况下,Babel 假定我们使用 es2015 模块编写代码,并转换 JavaScript 代码以使用 commonjs 模块。这样做是为了与服务器端 JavaScript 库的广泛兼容性,这些 JavaScript 库通常构建在 NodeJS 之上(NodeJS 只支持 commonjs 模块)。但是,Webpack 不支持使用 commonjs 模块来完成 tree-shaking。

现在,有一些插件(如 common-shake-plugin)声称可以让 Webpack 有能力对 commonjs 模块进行 tree-shaking,但根据我的经验,这些插件要么不起作用,要么在 es2015 模块上运行时,对 tree-shaking 的影响微乎其微。我不推荐这些插件。

因此,为了进行 tree-shaking,我们需要将代码编译到 es2015 模块。

es2015 模块 Babel 配置

据我所知,Babel 不支持将其他模块系统编译成 es2015 模块。但是,如果你是前端开发人员,那么你可能已经在使用 es2015 模块编写代码了,因为这是全面推荐的方法。

因此,为了让我们编译的代码使用 es2015 模块,我们需要做的就是告诉 babel 不要管它们。为了实现这一点,我们只需将以下内容添加到我们的 babel.config.js 中(在本文中,你会看到我更喜欢JavaScript 配置而不是 JSON 配置):

// es2015 模块的基本 Babel 配置 const config = { presets: [ [ '[@babel/preset-env]()', { modules: false } ] ] };

把 modules 设置为 false,就是告诉 babel 不要编译模块代码。这会让 Babel 保留我们现有的 es2015 import/export 语句。

**划重点:**所有可需要 tree-shaking 的代码必须以这种方式编译。因此,如果你有要导入的库,则必须将这些库编译为 es2015 模块以便进行 tree-shaking 。如果它们被编译为 commonjs,那么它们就不能做 tree-shaking ,并且将会被打包进你的应用程序中。许多库支持部分导入,lodash 就是一个很好的例子,它本身是 commonjs 模块,但是它有一个 lodash-es 版本,用的是 es2015模块。

此外,如果你在应用程序中使用内部库,也必须使用 es2015 模块编译。为了减少应用程序包的大小,必须将所有这些内部库修改为以这种方式编译。

不好意思, Jest 罢工了

其他测试框架情况类似,我们用的是 Jest。

不管怎么样,如果你走到了这一步,你会发现 Jest 测试开始失败了。你会像我当时一样,看到日志里出现各种奇怪的错误,慌的一批。别慌,我会带你一步一步解决。

出现这个结果的原因很简单:NodeJS。Jest 是基于 NodeJS 开发的,而 NodeJS 不支持 es2015 模块。为此有一些方法可以配置 Node,但是在 jest 上行不通。因此,我们卡在这里了:Webpack 需要 es2015 进行 tree shaking,但是 Jest 无法在这些模块上执行测试。

就是为什么我说进入了模块系统的“秘境”。这是整个过程中耗费我最多时间来搞清楚的部分。建议你仔细阅读这一节和后面几节,因为我会给出解决方案。

解决方案有两个主要部分。第一部分针对项目本身的代码,也就是跑测试的代码。这部分比较容易。第二部分针对库代码,也就是来自其他项目,被编译成 es2015 模块并引入到当前项目的代码。这部分比较复杂。

解决项目本地 Jest 代码

针对我们的问题,babel 有一个很有用的特性:环境选项。通过配置可以运行在不同环境。在这里,开发和生产环境我们需要 es2015 模块,而测试环境需要 commonjs 模块。还好,Babel 配置起来非常容易:

// 分环境配置Babel const config = { env: { development: { presets: [ [ '[@babel/preset-env]()', { modules: false } ] ] }, production: { presets: [ [ '[@babel/preset-env]()', { modules: false } ] ] }, test: { presets: [ [ '[@babel/preset-env]()', { modules: 'commonjs' } ] ], plugins: [ 'transform-es2015-modules-commonjs' // Not sure this is required, but I had added it anyway ] } } };

设置好之后,所有的项目本地代码能够正常编译,Jest 测试能运行了。但是,使用 es2015 模块的第三方库代码依然不能运行。

解决 Jest 中的库代码

库代码运行出错的原因非常明显,看一眼node_modules 目录就明白了。这里的库代码用的是 es2015 模块语法,为了进行 tree-shaking。这些库已经采用这种方式编译过了,因此当 Jest 在单元测试中试图读取这些代码时,就炸了。注意到没有,我们已经让 Babel 在测试环境中启用 commonjs 模块了呀,为什么对这些库不起作用呢?这是因为,Jest (尤其是 babel-jest) 在跑测试之前编译代码的时候,默认忽略任何来自node_modules 的代码。

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

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