模块副作用
Rstest 使用 Rspack 打包测试代码,可以在构建阶段跳过未使用的模块,减少需要构建和执行的依赖。像 lazyBarrel 这样的优化能加快测试启动,但前提是被跳过的模块没有必须执行的初始化代码。
sideEffects 声明告诉构建工具哪些模块的执行可以被跳过。本页介绍它与模块副作用、lazyBarrel 的关系,以及如何保留测试需要的初始化代码。
什么是模块副作用
模块加载时,除了提供导出值,还会改变外部状态的行为,就是这里所说的副作用。例如:
这个模块没有导出值,但执行它会注册 mock。其他常见的副作用还包括修改全局变量、安装 polyfill 和注册事件监听器。
下面的 bare import 用来执行模块的初始化代码:
sideEffects 如何影响模块执行
package.json 中的 sideEffects: false 告诉 bundler:这个包中的文件可以在不使用其导出值时跳过执行。
当这个声明适用于注册模块,且启用相应优化时,即使源码写了 import './register-mocks',这个导入仍可能被移除。sideEffects 是构建工具会信任的声明;文件中实际包含注册代码,并不意味着它一定会被保留。
详见 Rspack 的 tree shaking 文档。
Rstest 如何利用 lazyBarrel 加快构建
Rstest 将测试文件及其依赖交给 Rspack 构建,因此可以直接利用 Rspack 默认开启的 lazyBarrel 优化,无需为此切换到生产模式。Rstest 不会自动将业务代码标为无副作用,优化依赖项目或依赖包提供的 sideEffects 声明。
barrel 文件是集中重导出其他模块的入口。测试经常只使用其中一小部分导出,例如:
当 src/utils/index.ts 声明为无副作用,且构建图中没有其他模块使用 formatDate 时,Rspack 可以沿 sum 的重导出构建 sum.ts,跳过 format-date.ts 分支。
因此,收益不只是最终 bundle 中少了一些代码,而是这些无关分支可以不进入后续构建处理。对于导出很多组件或工具函数的入口,测试只使用少量导出时,可以减少冷启动构建的工作量。watch 重新构建时也会按依赖需要处理模块;它与复用已有构建结果的缓存机制是不同的优化。
如果其他测试或模块也使用了 formatDate,这个分支仍然需要构建。若通过 export * 重导出,Rspack 可能需要先构建更多模块才能确定导出来源,收益也会受到限制。
sideEffects 是模块的声明,lazyBarrel 是利用这个声明的优化。 lazyBarrel 在构建早期就要决定跳过哪些分支,因此依赖 package.json 或 module.rules 中的显式声明,无需先构建整条依赖链来分析副作用。
如果被跳过的分支中实际包含初始化代码,错误的 sideEffects: false 声明就会让这些代码不执行。应为这些模块及必要的中间导入模块正确声明副作用。
此外,Rspack 还可以跳过声明为无副作用的 bare import 的构建,例如 import './register-mocks'。这是相关的独立优化,不属于 lazyBarrel;即使没有 barrel 文件,错误的声明也可能让初始化导入被跳过。
详见 Rspack 的 barrel 文件优化。
声明需要执行的模块
只有初始化模块被错误地标记为无副作用时,才需要修正相关声明。未设置 sideEffects 不等于设置了 sideEffects: false,无需仅因为使用 Rstest 或把注册代码放在单独文件中,就额外添加副作用配置。
在 package.json 中声明
如果项目原来声明了 sideEffects: false,但部分文件需要执行初始化,可以改用数组列出这些文件:
路径相对于这个 package.json。数组以外的文件仍然按无副作用处理。修改已有数组时,要保留其中其他仍有副作用的文件。
在上述数组声明下,如果初始化通过多层导入触发,例如 test → bootstrap → helper → register-mocks,需要确保路径上的中间模块也不会被跳过。只把最后的注册模块标为有副作用,可能仍无法保留整条导入链。将这类文件集中到 test/support 目录,再按目录声明,是一种方便维护的方式。
在 Rstest 配置中声明
如果只想在测试构建中覆盖无副作用声明,可以通过 tools.rspack 为指定目录设置 sideEffects: true:
规则匹配的是被导入模块的路径。sideEffects: true 表示保留它的副作用;设为 false 则允许跳过它。这个配置不会主动加载目录中的所有文件,文件仍然需要被导入。
精确匹配初始化目录,可以继续让其他模块参与 barrel 优化。规则语法详见 Rspack 的 module.rules。
第三方依赖的范围
以上优化作用于参与打包的模块。交给 Node.js 在运行时加载的 external 依赖,不会被 Rspack 按这些规则裁剪。
第三方依赖是否参与打包由 output.bundleDependencies 等配置决定。调整打包范围会改变哪些模块受到 sideEffects 声明影响。