跳到主要内容
版本:1.0.0

主包体积优化

背景

互联网时代中人们对信息的需求逐渐趋向快捷化、方便化、效率化,为了适应快节奏的社会发展,小程序应运而生,作为快时代的产物,各平台小程序逐渐进入大众用户的视野,成为人们生活中不可或缺的一部分,不用安装 APP,不用记住网址,随用随取简单方便。

小程序作为轻量级应用,为了保障顺畅运行,各平台对于小程序体积大小有着严格的规定,而随着业务需求的不断累积,通常小程序的体积也在不断增大,官方也推出了支持小程序分包加载功能,将小程序划分成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载,但是每个使用分包小程序必定含有一个主包。(关于主包分包的概念可参考文档:复杂小程序集成-概念-模块类型

以目前几个主流平台为例,支付宝、微信小程序都要求单个分包/主包大小不能超过 2M,所以对于优化小程序主包,降低小程序主包大小,成为大多数开发者都将面临的问题。

动机

主包中并非所有组件、文件都会被主包及多个分包使用,部分公共 js 代码、node_modules 代码和 npm 组件文件可能仅在某一个分包中使用,只要通过解析各文件依赖,建立分组关系,在编译时动态的将文件调配到不同分包中去,让分包自己的模块仅编译到自身路径内,仅分包间共享的模块才会进入主包,对于从未使用(引用)的代码或模块在编译时并不会一同打到产物中,以此用于优化主包体积。

相比与主包需要尽可能压缩自身体积,分包显然自身容量有较大的余留,那么把各个分包依赖的内容直接编译到对应分包中去,可以最大化利用分包自身的容量大小,在主包中由于该依赖内容不再有相关使用(引用),根据文件依赖的分析,在编译时这些已被冗余到各个分包的依赖就不会被一同打到主包产物中,减小主包体积的同时提升分包利用率。

目标

  • 建立完整的文件依赖关系树和各分包分组信息模型
  • 提供编译时的动态分组优化能力,动态调配不同分包文件
  • 为独立分分包编译提供完整的冗余编译方案

概念

模块依赖图

  1. 模块依赖图:主要用于 Entry 构建的依赖关系建立和动态分组,包含一个小程序主包 mainGroup、一个根模块 rootModule(用于形成带顶端的树状结构, 可减少循环次数及明确父类归属)、N 个普通 Module(随 Entry 分析及文件变化动态删减)、N 个普通 Group(用于各个分包)、N 个 无效的 Module(用于标记被变更或被删除的模块);
  2. 普通 Module:一个文件是一个 module,以文件的全路径作为 module 标识,包含 文件地址、依赖信息、被依赖信息 及 分组信息;
  3. 普通 Group:模块的分组信息,一个分包对应一个组,包含名称以及相关的模块;

分包动态编译优化

分包编译时动态优化,是在编译过程中,通过文件依赖树及分组关系,动态的将文件调配到不同分包中去,比如仅某一个分包使用的 npm 组件提取到自己分包中,以及 node_modules 自动提取到分包中等,分包间共享模块会进入主包,分包自己的模块会进入分包。

独立分包冗余编译

将分包依赖的内容直接编译到对应分包中去,不论这些依赖是否和已有的分包有重叠,也不管是不是主包的组件,都会以冗余的方式编译到每个分包中去,最大化利用分包自身容量大小。

方案

模块依赖图

一个完整的项目,经过模块依赖图的分析,会给每个文件 Module 进行相关的依赖分析和分组,以文件的全路径作为唯一标识,分析构建其依赖图,对于各分包的文件分组到各自对应分包,对于主包文件若是被多个分包共享依赖则分组到主包,若是仅被某一个分包使用则会动态分组到该分包,而通过这些文件 Module 、分组 Group 等结构组成一张完整的模块依赖图,编译时通过各个文件的文件依赖树和分组关系,对应 entries 的文件信息进行编译产出产物。

分包动态编译优化

分包在执行编译的过程中,编译每个文件时会通过文件依赖树找到对应依赖及其文件信息,根据两者的分组关系,动态将公共 js 代码、node_modules 代码和 npm 组件文件调配到不同分包中去,生成该分包内的公共模块文件,而拆分出来的文件则因为在主包中不被任何其他模块依赖,将从主包编译中删除

独立分包冗余编译

将分包依赖的内容直接编译到对应分包中去,不论这些依赖是否和已有的分包有重叠,也不管是不是主包的组件,都会以冗余的方式编译到每个分包中去,最大化利用分包自身容量大小。