Taro 4.2.1 使用 Vite 构建微信小程序时 common.js 为什么这么大
一次生产构建后,根目录下三个文件的体积差得很远:
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.js,common.js 里主要是项目自己的共享代码。
common.js 是怎么生成的
Taro 4.2.1 使用 Rollup 的 manualChunks 决定公共文件。原始实现就在 @tarojs/vite-runner 的 getManualChunks 中,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-vendors 或 sub-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,不会再问“为什么每个页面都打了一份”。它只有一份。该查的是:哪些代码被多个入口引用,又有哪些代码其实不必留在主包。