据我所知,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 的代码。
这实际上是件好事。如果 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 重新编译你的库所需要的。有两个主要部分,我会一一解释。