[TOC]
# 1. 开始
项目由基于 Vue2 的 uni-app 迁移到了 Vue3 的 uni-app,遇到了主包大小的问题。
原因在于项目模式是“大组件,小页面”,且使用了较多的 npm
包,比如 Press Plus
等。Press Plus
、local-component
、component
子仓的位置都在分包以外,按照小程序的打包规则(基于目录位置),自然是放到主包的。当使用越来越多的外部组件时,必然会遇到主包过大的问题。
Vue2 时已经写了基于 Webpack 的组件分发和脚本分发插件,这次来实现 Vite 版本(Vue3)的组件分发插件,脚本分发插件后面看情况再开发。
# 2. 思路
把大象放进冰箱,需要3步:
- 打开冰箱门
- 把大象放进去
- 关闭冰箱门
组件分发比上面简单,需要2步:
- 找到需要分发的组件
- 分发
如何找到需要分发的组件:
- 获取依赖关系,可以通过遍历
bundle
(generateBundle
钩子中)实现 - 获取全局组件、主包、分包信息,可以通过读取
bundle['app.json']
实现 - 根据依赖关系、分包列表等,分析数据,拿到需要分发的组件,以及要分发到哪里
如何分析数据:
- 依赖关系指的是“子子组件A => 子组件B => 页面C”这种关系
- 需要递归分析吗?必须需要。如果不递归,只能拿到第一层或者前几层的引用关系,无法得到使用一个组件的所有分包
- 分析时,首先能拿到的是“父引用子”,需要先递归,然后翻转,翻转就是让“子节点当作键”(理论上先翻转后递归也可以)
- 递归分析,只要没被主包页面引用,就是被分包使用了。前提是组件是被使用的。就是从子节点到根结点,根结点一定属于某个分包或主包的一个页面。
列一下符合规范的引用关系类型:
- 主包组件 => 主包页面(直接引用)
- 主包组件 => ...主包组件... => 主包页面(间接引用)
- 主包组件 => 分包页面(直接引用)
- 主包组件 => ...主包组件.... => 分包页面(间接引用)
- 主包组件 => ...分包组件... => 分包页面(间接引用)
- 主包组件 => ...主包组件... => ...分包组件... => 分包页面(间接引用)
- 分包组件 => 分包页面(直接引用)
- 分包组件 => ...分包组件... => 分包页面(间接引用)
其中类型 3、4、5、6 都是需要分发的。
分发步骤:
- 移动组件到目标位置
- 修改引用关系
- 删除原有组件
修改引用关系又分为:
- 修改之前引用者的引用关系,比如“子组件=>分包页面”中的分包页面
- 修改移动组件内部的引用关系,比如“子组件=>分包页面”中的子组件
第1步是精确替换,只替换对移动的组件的引用关系。第2步是全量替换,对所有引用关系进行替换。
第1步中,对于"主包组件A => 主包组件B => 分包页面C"这种关系,A被移动了,需要替换B中的引用关系吗?
其实是不需要的(第1步),如果A被移动了,说明B一定也被移动了,否则就会反向依赖,而这在数据分析阶段就已经规避了。
主包组件B既属于1,又属于2,也就是既是被移动的组件,又是其他移动组件的父组件。需要在第一步排除(不处理),并在第二步的时候额外判断,取"子子组件"移动后真正的路径。
# 3. 核心点
几个核心点:
- 递归分析引用关系,以及避免循环引用
- 找到需要派发的组件,以及目标位置
- 替换引用时,需要对移动组件内部的引用关系进行替换。这里用了正则去匹配,然后根据之前的组件位置、新的组件位置、之前的引用路径,生成新的引用路径。
# 4. 效果
在一个项目中使用了,并且配置了只有一个分包使用时才移动,主包能够减少 0.8MB
,降低了 41.5%
,并且由于只移动了最需要移动的,所以总包大小基本没有变化(变化的10K左右是由于生成的路径变深)。
如果把 limit
设置为0
,也就是只要主包不使用,就移动到分包中,可以将主包减少到 1015KB
,降低了 47%
。但由于多个分包中存在重复组件,总包也会增加。
使用方可以根据自身项目需要,灵活设置此选项。