Taro 4.2.1 使用 Vite 构建微信小程序时 common.js 为什么这么大

太阳作者太阳
原创内容采用 CC-4.0 协议发布,转载请注明出处
TaroVite微信小程序构建优化Rollup

一次生产构建后,根目录下三个文件的体积差得很远:

  • common.js:875,461 B;
  • taro.js:212,660 B;
  • vendors.js:14,181 B。

主包原始文件合计约 1.44 MiB,全部包约 1.60 MiB。页面文件反而都很小。

我先怀疑是不是某个 npm 包被塞进了 common.js。这个方向不对。Taro 的 Vite 构建器已经把普通第三方依赖分到了 vendors.jscommon.js 里主要是项目自己的共享代码。

common.js 是怎么生成的

Taro 4.2.1 使用 Rollup 的 manualChunks 决定公共文件。原始实现就在 @tarojs/vite-runnergetManualChunks 中,React 分支的判断顺序是:

if (testByReg2DExpList([taroDeps, reactRelatedDeps, tslibDeps])(id)) return 'taro'
if (testByReg2DExpList([nodeModulesDeps])(id)) return 'vendors'
if (moduleInfo?.importers?.length && moduleInfo.importers.length > 1) return 'common'

这几行已经把文件名解释清楚了。Taro 和 React 运行时进入 taro.js,其他 node_modules 依赖进入 vendors.js。剩下的项目模块只要有两个以上直接导入方,就会进入 common.js

common.js 大,不是因为这条规则制造了代码,而是项目中满足条件的共享代码加起来就有这么多。规则只是把它们集中到一个文件里。

这里还不能继续猜“哪个模块最大”。当时没有保存 visualizer 报告,只留下了总量和大致组成。没有同一次构建的模块体积数据,给具体模块排大小没有依据。

页面引用 common.js 不等于复制一份

构建后的页面文件会引用根目录公共块:

const taro = require('../../taro.js')
const common = require('../../common.js')
require('../../babelHelpers.js')
require('../../vendors.js')

目录更深的页面只是相对路径不同。上传产物中仍然只有一个根 common.js,不会因为二十个页面引用它就变成二十份。

麻烦在于它位于主包。Taro 4.2.1 这段 manualChunks 只检查模块路径和直接导入方数量,没有继续判断这些导入方属于主包还是分包。于是,只在分包里复用的项目代码,也可能被提取到根 common.js

这正好解释了眼前的现象:分包已经存在,页面也确实很小,但主包中的公共业务代码仍然很集中。

optimizeMainPackage 为什么没起作用

Taro 的 mini.optimizeMainPackage 配置文档 描述了另一种结果:只在分包中使用的公共模块可以进入 sub-vendorssub-common,从而缩小主包。

配置写法很简单:

mini: {
  optimizeMainPackage: {
    enable: true
  }
}

但在这个 Taro 4.2.1 + Vite 项目里,打开前后的结果几乎一样:

  • common.js 从 875,461 B 变成 875,505 B;
  • 没有生成 sub-vendors.js
  • 没有生成 sub-common
  • 页面仍然引用根目录公共块。

44 B 从哪里来,我没有做重复构建,不能下结论。能确认的是,文档所说的分包公共文件没有出现。

奇怪的是,TypeScript 并不报错,因为 mini.d.ts 确实声明了 optimizeMainPackage。可是真正执行 Vite 构建的 config.ts 中,没有读取这个配置;Rollup 最终使用的仍是前面的 getManualChunks()

也就是说,字段能写、类型能过,只能说明 Taro 的配置类型里有它。当前 Vite runner 是否执行,还得看 runner 本身。

最后怎么处理

我把 optimizeMainPackage 撤掉了。一个不生效的配置留在项目里,比没有配置更麻烦,后来的人会默认这项优化已经打开。

接下来如果还要压主包,先在固定提交上做一次干净构建并保存 visualizer 报告。拿到模块体积和导入关系后,再决定是调整页面归属、减少共享入口,还是只为微信小程序评估 Webpack 5。现在直接改 manualChunks,最多只是把一个大文件换成几个文件名,未必能减少主包总量,还可能碰到 Taro 源码注释里提到的 CommonJS 循环依赖。

所以我现在看 common.js,不会再问“为什么每个页面都打了一份”。它只有一份。该查的是:哪些代码被多个入口引用,又有哪些代码其实不必留在主包。