# 代码分包(通用适配线)
# 概述
# 背景
使用通用引擎适配方案(下文简称通用适配,含通用适配转换工具与通用适配 SDK)把 H5 游戏包转换并集成后,代码位于一个 wasm 文件里,经过 brotli 压缩后,放在 wasmcode 目录下。
一般小游戏的 wasm 大小为 30M 左右,压缩后为 6M 左右。
启动阶段,小游戏需要先下载完 wasmcode 再编译,这里会占用较高的内存和启动时间,这两者对用户都是非常重要的指标。
因此我们提供了 Wasm 代码分包工具,将原来的 wasm 拆分为主包(用于启动加载)和子包(延迟加载),使得小游戏可以先加载较小的首包进入主场景,再异步加载剩下的分包,大幅度降低下载时间和编译耗时。
另外,iOS 高性能模式下子包转为按函数粒度按需加载,从而大幅度减少内存压力,降低崩溃。
本文先介绍通用适配方案与分包插件的对应关系,再介绍操作步骤,最后介绍开发者长期迭代时所需关心的核心指标以及问题。
# 通用适配方案与分包插件的对应关系
微信小游戏的引擎适配方案经历了两个阶段:
| 阶段 | 支持的引擎 | 导出 / 转换工具 | 分包插件 | 启动插件 |
|---|---|---|---|---|
| Unity 专用方案 | 仅 Unity / 团结引擎 | Unity / 团结引擎导出插件 | wasmCodeSplit 插件(+ wasmsplit-ci) | UnityPlugin |
| 通用适配方案 | Unity 以及 Unity 之外的引擎(UE、Cocos2d-x、自研引擎等) | 通用适配转换工具 + 通用适配 SDK | Wasm代码分包V2 插件(+ wasmsplit-v2-ci) | WXGameKit |
通用适配方案同样支持 Unity,并且额外支持 Unity 之外的引擎。它对导出后的项目结构做了调整,因此使用通用适配方案导出的小游戏,必须搭配 Wasm代码分包V2 使用,不能使用 Unity 专用方案下的 wasmCodeSplit 插件。
请先确认你的游戏使用的是哪种导出方案:查看小游戏项目 game.json 的 plugins 字段——为 WXGameKit 则使用的是通用适配方案,继续阅读本文档;为 UnityPlugin 则使用的是 Unity 专用方案,请改阅对应的代码分包文档。
两条线对应的分包工具如下。通用适配没有版本分支,只能搭配 Wasm代码分包V2;Unity 专用方案内部则按 UnityWasm SDK 的版本号区分配套的分包插件:
两条线使用的分包插件不可混用:用哪种方案导出,就用该方案对应的分包插件。
# 实现原理
目前我们采用了一种 Profile Guided Optimization 的方式,通过运行时收集信息,按函数粒度对小游戏的 wasm 代码包进行拆分。
开发者可以在开发阶段,通过真机运行小游戏,并尽量覆盖游戏内的场景,特别是启动后最先进入的场景和关卡(比如新手教学、游戏最初的关卡内容)来收集信息。工具上会显示收集到的函数个数,这时候就可以重新分包,将收集到的函数加入首包。
因此收集工作非常重要,收集的场景覆盖率越高,命中子包的时机就可以相应延后,使得首包可以满足大部分新玩家前几分钟的游戏进程。
分包技术会持续迭代,请确保使用最新的分包插件以及通用适配导出/启动插件,以获得最优的分包性能体验。
# 使用前提
在开始分包之前,需要在导出阶段做好以下准备:
代码包命名与 md5:通用适配转换工具在转换时会计算 wasm 的 md5(取前 15 位)并写入游戏侧
config.json的launch.code.fileMD5,代码包文件命名为<md5>.wasm.br,放在wasmcode目录下。分包工具通过这个 md5 区分不同的小游戏包。symbol 文件(增量分包需要):如果后续需要使用增量分包,导出时需要生成 symbol 文件。通用适配线通过导出参数
--symbols-file传入符号文件,导出插件会把它转换成 JSON 格式产出在工程内(默认在wx-game-kit/下,文件名形如<项目名>.js.symbols.json),插件初始化时会自动扫描到。增量分包依赖该文件来复用历史收集结果,因此需要确保前后两个包都提供 symbol 文件。插件扫描工程时会接受
*.symbols.json(规范格式:{ 函数索引: 函数名 })、symbols.json、*.symbols、*.symbols.unityweb几种命名的文件;其中 Unity 风格的.symbols(每行0:name)不满足规范,会让增量分包继承失败。多个候选共存时优先取.symbols.json。
# 插件安装
通用适配方案对分包插件与微信开发者工具的版本有要求,请使用最新版本的分包插件与微信开发者工具。
在微信开发者工具的「扩展」视图(左侧活动栏的扩展图标,或快捷键 ⇧⌘X)里安装:
- 在搜索框搜索
Wasm代码分包V2(发布者 minigame,扩展 IDminigame.wasmsplit-v2),点击安装;
安装或更新完成后,如果列表项上出现「Reload Required」(需要重新加载),重启微信开发者工具即可生效。
为了保证正确性,推荐关闭分包后再更新插件。
# 插件更新
开发者工具上的插件一般会自动更新,通常不需要手动更新。可以在「扩展」视图的「已安装」列表里查看当前插件版本,确认是否为最新版本。
# 插件使用
分包插件按小游戏的 md5(wasmcode 下 wasm 文件名里的 md5)来区分不同的小游戏包,相同 md5 的小游戏包会复用同一份分包结果。
对于新构建的小游戏包,分包流程如下:
- 启用分包(项目初始化)
- 输入当前项目版本描述
- 等待上传文件
- 等待预处理
- 选择是否增量分包,然后下一步
- 等待第一次分包
- 进入正式分包阶段,在鸿蒙、安卓、iOS 设备上分别收集,每收集一轮点继续下一步
- 选择生成 Profile 或者 Release 包,然后继续收集,这一步可以重复进行
最后一步完成后即可随时上传体验版。
具体操作和注意事项如下。
# 启用分包(项目初始化)
打开分包插件开关后,在目录树上的工具栏中可以看到插件的按钮,点击后即可进入插件页。
进入插件后,先在「项目初始化」页确认分包所需的关键路径:
- Code MD5:当前代码包的 md5,用于区分不同的小游戏包,相同 md5 会复用同一份分包结果。插件会自动扫描填入,不建议手工修改——修改会改变配置与文件名,且无法还原。
- Wasm 文件路径:即
wasmcode/<md5>.wasm.br。 - symbols 文件路径:symbol 文件,增量分包依赖它(见「使用前提」)。
配置为空时进入页面会自动扫描,也可以点击「重新扫描」。确认无误后点击「开始使用」。
# 输入版本描述
# 等待上传文件
# 等待预处理
这一步一般是分钟级的耗时(插件后台约每 10 秒轮询一次进度),如果等待时间明显过长(超过 10 分钟),建议先重启开发者工具再试。
# 选择是否增量分包
如果这个游戏之前的包已经使用过代码分包,且没有比较大的代码修改(比如大范围代码重构、更换引擎),可以选择增量分包(界面上的选项名为「选择增量更新」),即选择之前的某个包作为参考(referMd5)。工具会对比前后两个包的 symbol,把之前收集过的函数直接放入首包,无需重复收集。
因此增量分包要求前后两个包都必须提供 symbol 文件(见「使用前提」)。导出时通过 --symbols-file 传入符号文件即可生成 symbol(对应 Unity 线的「开启 profiling func 导出 symbol」,无需担心 symbol 导致的包体增大,分包工具会优化函数名)。
参考版本在选定后无法再更换:首次分包选「不依赖任何历史版本」,之后每次分包沿用首次选定的参考版本。
# 等待分包
分包阶段界面约每 10 秒查询一次后台进度,单次请求连续失败 3 次会中止本次等待。界面另设 10 分钟安全上限,超过会提示「分包处理超时(10 分钟)仍未就绪」,需要重新发起。界面提示通常需要 1~5 分钟:Profile 分包一般 1~2 分钟,Release 分包更慢,可能到 7 分钟以上(同一个 AppID 上还有别的任务排队时更久,会贴近该上限),等待期间不要重复点击提交。
# 鸿蒙 / 安卓 / iOS 收集
收集页上有两块与收集范围相关的文案:
- 收集提醒(常驻,不随迭代次数消失):请在鸿蒙、安卓、iOS 设备上分别运行小游戏,完整收集各平台的函数调用数据;各平台实际调用的函数不同,缺少任一平台的数据会让该平台在运行时更多地等待子包加载。
- 迭代指引(当前分包版本小于 3 且没有进行中的分包任务时显示):请在真机上运行小游戏,收集函数调用数据;新增函数增长后,点击「继续分包」优化分包效果;建议至少完成 3 次分包迭代。
各平台的函数调用情况不同,一般需要分别收集。
收集操作:
- 点击开发者工具的预览(注意是预览,不是真机调试),在真机上跑游戏,覆盖尽可能多的场景
- 当插件页显示的新增函数个数相对稳定时,点击「继续分包」
这里主要关注首包函数个数。一般跑完各平台的收集后,首包函数占整包函数个数的比例达到 33% 以上即可(参考「评估分包状态」一节)。
# 重复收集与生成
各平台收集完,基本就可以测试和发布了。有条件的话,可以尽量覆盖各种机型(主流品牌)再多跑几次收集。
每次收集发现新增函数趋于稳定之后,就可以点「继续分包」生成 Profile 分包或者 Release 分包(生成哪一类由「下次分包配置」里的分包类型决定)。点击后停留在收集页,页面下方会显示「等待分包完成」,完成后回到收集态。
每次生成,当前的分包 version 会加 1。
# 函数名获取与手动上报
为了方便开发者分析首包函数来精简代码,插件的「辅助功能」分组提供了两项能力:
- 获取首包和缺失函数(按钮「获取」):下载并打开两个 txt 文件,分别包含首包函数名与缺失函数名(对应 CLI 的
showfuncname命令)。 - 通过文件上报缺失函数(按钮「文件上报」):选择一个 .txt 文件(每行一个函数名)上报其中的全部函数,上报格式与「获取」下载的文件一致。切换 appid 后可以用它恢复增量分包,也可以为自家游戏预先准备一份函数名文件手动上传来节省真机收集时间(除非确定需要,否则不建议)。
# 关闭分包
如果想回退到未分包的版本,点击「关闭代码分包」即可:工具会把工程恢复成原始小游戏包文件的状态(还原 game.js 与 game.json,并移除分包产物与分包配置),此前的分包结果与收集数据仍保留在后台,下次开启分包并重新下载即可恢复分包状态。切换代码 md5、重新导出游戏包之前,也需要先关闭分包。
# 注意事项
上线前:
- 生成 Release 版分包。
首包函数至少要有总包函数量的 33% 以上(参考「评估分包状态」一节)。新增函数清零。- 关注 android 或 iOS 普通模式的子包是否过早加载(是否有「首次拉取js函数」「在callmain完成前fetchjs」「subwasm在callmain完成前加载」等日志)。
- 关注 iOS 高性能是否有「首次拉取js函数」等按函数拉取的日志。
如果有最后两点,那就还要继续收集 + 生成分包。
上线后:
- 小游戏包先不要删掉。
新增函数这个数据非常重要,要尽量保持关注,包括上线后。
如果上线后,有玩家遇到新增函数个数,分包插件上也会更新(主要来源是 iOS 高性能),或者收到小游戏数据助手的相关告警,这个时候要继续生成分包,同时提审发布。
# 分包关键信息
# 分包基本信息
分包基本信息分布在插件上的两处。
「分包信息」面板(插件顶部,可展开):
- AppID:面板取值来自工程
project.config.json,即接口实际使用的身份。如果它与本次分包会话记录的 AppID(初始化时从wx-game-kit/config.json的project.app.appID回填并存进插件配置)不一致,面板会多出一行提示,建议关闭分包后重新初始化,避免两处 appid 不同导致分包记录对不上。 对同一个 AppID 的任何分包操作都会排队进行,团队内同时开发时可能出现耗时变长的情况,请耐心等候。 - MD5:小游戏项目的唯一标识,取自
config.json的launch.code.fileMD5,相同的 md5 被识别成同一分包项目。 - 分包版本:代表第几次分包。第一次分包为 0,第二次为 1,以此类推;-1 表示从未分包。
- 后台服务版本:为分包的技术版本,代表不同的分包技术,越新越好(当前仅支持 7 与 9 两个取值)。
- WXGameKit 版本:通用适配启动插件的版本,对应
game.json中plugins字段里的 WXGameKit 版本。注意它并不是分包插件本身的版本。 - 分包插件版本:当前安装的分包插件版本。
面板上还有「一键复制」,可一次复制以上信息,便于反馈问题时提供环境信息。
「分包收集」页的「分包状态」:
- 总包函数量:开发者原始 wasm 包的函数个数。
- 首包函数:当前首包 wasm 中函数的个数。
- 新增函数:本次收集中发现未在首包的函数。
- 已开启优化:表示本次分包已生效的优化项,为空则表示没有开启任何优化项。可在「微信优化项」勾选,勾选后作用于下一次分包,因此它可能与上方「分包状态」中已开启的内容不一致。
# 分包项目结构
分包后项目结构如下(编辑器资源管理器视图):
主要关注以下文件:
wasmcode/:分包前是原小游戏 wasm 包;分包后是首包 / 主包,一定会被加载且首先被加载。包内 wasm 文件命名为<md5>.wasm.br(.wasm.br为 Brotli 压缩格式),同目录下还有一份该包的game.js。wasmcode1/:子包,启动阶段不下载,在游戏运行一段时间后自动加载,或在缺失函数时加载。目录内同样是<md5>.wasm.br加一份game.js。wasmcode2/(可选):部分分包技术下会有的二级子包。wasmcode/<md5>.webgl.redirmem.bin:重定向内存表,分包运行时的辅助文件。wasmcode/<md5>.webgl.import.dat(可选):辅助文件。wasm-split.js(项目根目录,与wasmcode/同级):分包运行时脚本,由分包工具写入;game.js里的注入逻辑会 import 它。旧版本放在wx-game-kit/下,升级到新版插件后统一落到项目根目录,还原时旧位置的残留也会一并清理。- symbols 文件:位于
wx-game-kit/下,如wx-game-kit/<项目名>.js.symbols与wx-game-kit/<项目名>.js.symbols.json,用来还原每个函数 id 对应的函数名,请保留,有需要的时候查看。插件识别符号文件时会同时接受*.symbols.json/symbols.json/*.symbols/*.symbols.unityweb几种命名。
说明:
- 分包后项目大小会膨胀,这是正常现象。分包需要一些辅助文件,但只有首包(wasmcode)一定会加载,其他的则会延迟或者按需下载,对运行几乎没有影响。
wasmcode/wasmcode1/wasmcode2三个目录名在单包工程里是固定命名,不可配置。- 分包工具会向
game.js注入相关逻辑,并在game.json中补充分包声明(subpackages),请勿手工改动这些自动生成的内容。 - 当 wasm 代码包超过 20MB 时,会触发「code oversize」上报,建议关注包体大小。
多包融合(多个小游戏包合成一个小程序包)说明:融合形态下游戏代码树位于各自的子目录(如 minigame/、minigame-iOS/),子包名也会带条件后缀(如 wasmcode1-iOS)。新版运行时模板会从 wx-game-kit/config.json 的 launch.code.moduleName 与 launch.code.gameDir 推导出子包名与路径,无需人工改动模板;这两个字段缺省时按单包处理,行为与改造前一致。主包 wasm 的路径由启动插件决定,不由分包工具改写。
包体大小的经验参考:首包 3~5MB,子包 7~15MB,wasmcode + wasmcode1 + wasmcode2 合计 10~25MB。以一个 UE 小游戏工程为例(总包函数量 124,393、尚未收集函数):首包 2.7MB、wasmcode1 9.0MB、wasmcode2 12.1MB,合计 23.9MB。
# 微信优化项
分包集合了许多定制的优化项,与游戏特征有关,按需选择:
Profile 分包下,「Profile包性能优化」默认勾选(见「选择是否增量分包」一节的截图);Release 分包下该行不再展示:
先说明分包产物的两种类型:
- Profile 包:带函数调用监控,用于收集;性能和内存表现差,严禁上线。
- Release 包:无监控,用于发布。
优化项:
- Profile包性能优化(logCallInWasm):修改函数调用上报的记录方式,使得运行性能接近 Release 包,提高收集函数的效率。选择 Profile 分包时默认勾选(手动取消后本次会话不再自动补勾,扩展重启恢复默认);选择 Release 分包时这一行不展示,并按关闭提交。
- 函数量优化(mergeSmallFunc):合并小函数,平衡主/子包的函数分布,可进一步减小分包后 wasm 包中的函数量,从而减小编译内存占用。该优化项标注为 (Beta),对运行性能有 0~5% 的负面影响、内存占用降低 0~50MB,与游戏代码特征有关,需要游戏自行测试收益并选择。
勾选项作用于下一次分包,因此可能与上方「分包状态」中「已开启优化」的内容不一致。
优化项需要后台服务版本与插件版本达到要求才会生效。插件本身不做版本校验,只是把开关透传给后台;勾选后若「已开启优化」没有相应变化,请把分包插件与微信开发者工具更新到最新版本。
# 评估分包状态
开发者在函数收集 / 上线后,都需要关心分包是否到位,并及时做出响应。
# 上线前的分包收集
函数收集需要保障启动时刻的函数收集。在启动和游玩的前期,是用户最容易流失的时候,必须保证函数完全不缺失,否则缺失会造成卡顿,影响游玩体验甚至启动速度。
可以通过 vConsole 以及日志中是否有以下日志,来判断当前是否出现了函数的缺失:
首次拉取js函数(启动插件 WXGameKit 打印):表示首次按函数粒度拉取缺失函数的时机。如果出现得过早,说明收集不够。在callmain完成前fetchjs(WXGameKit 打印):表示函数缺失甚至在引擎启动阶段就出现了,这会极大影响游戏的启动时间,需要及时重新收集。subwasm在callmain完成前加载(WXGameKit 打印):表示子包在引擎启动阶段就被加载,说明首包收集不足。fetchjs等待耗时: <毫秒>(WXGameKit 打印):单次按函数拉取的等待耗时,明显偏大说明该函数本该在首包。缺失函数过多(WXGameKit 打印):按函数拉取的等待时间过长(超过阈值),需要继续收集。wait for func: <tableId>(分包运行时wasm-split.js打印):运行时缺少某个函数、转为按函数拉取。子包已加载、instantiatePatch完成后仍频繁出现,说明首包收集不足。Wasm split func missing report: ...(分包运行时打印):运行时把缺失函数上报给后台,用于后台统计与 Patch;出现频率高时同样建议重新收集。
说明:前五条来自启动插件 WXGameKit,后两条来自分包运行时模板
wasm-split.js。不同版本打印的措辞可能略有差异,以实际日志为准。
如果收集过程中出现了上述问题,需要及时收集并重新分包,保证这些函数都在首包内。
由于不同机器有一定差距,因此会出现一个机器收集后、另一个机器出现缺失的情况。这是正常的,开发者需要根据实际情况(例如线上启动时间的变化),选择是否需要重新收集 / 分包 / 上线。
越多的函数在首包,首包就会越大,启动时间和占用的内存也会越多。但就现在游戏的经验来看,收集得越充分越好,因为大部分函数不会被收集到,分包的优势仍然非常大。但请开发者不要盲目手动上报一些不缺失的函数,这样会影响性能表现。
如果开发者发现,自身的游戏在不同场景上运行到的函数数量有巨大的差别(上万个函数的差别),统一打包到首包会使首包特别大,请联系小游戏助手获取技术上的帮助。
经验上首包函数占整包函数的 25%~50%,一般 35% 左右;达到 33% 以上即可接受。
# 上线后的缺失 / 收集 / 补救
需要首先强调的是,Profile 分包严禁上线,性能有非常大的折损,请用 Release 分包上线。
我们在线上的分包运行时,也插入了比较稀疏的上报,能够在线上缺失的时候自动收集函数。因此,游戏上线之后,开发者仍然能通过分包面板看到自身的新增函数个数。
微信分包平台为保证用户体验,采取了 Patch 包机制,自动根据收集的新增缺失函数生成 Patch 包,自动修复缺失函数。但该机制与机器的系统版本、网络环境、分包插件依赖组件的版本等有关,并不一定能在所有玩家设备上成功加载,Patch 包也有大小上限。因此我们主张开发者在函数量过多的时候,主动进行分包并重新上线。
新增函数个数超过 100 个时,建议主动分包并重新上线。
对于长期迭代的开发者来说,还可以使用增量分包。但增量分包是基于函数名进行匹配的,而由于原函数到 wasm 函数有较长的一段编译器生成距离,同一个函数即使没有改动,其名字和结构仍然可能变化。因此,增量分包并不一定会完全覆盖原有的所有函数。我们主张开发者尽可能保障启动黄金期,保证函数在首包内。
# CI 工具
Wasm 代码分包作为微信开发者工具的插件,需要开发者在开发者工具中手动操作,在高频发布场景下效率偏低。
因此我们提供 wasmsplit-v2-ci 工具,可以不打开开发者工具,独立使用进行分包的各种操作(真机收集除外),供开发者集成到 CI 流水线上。
# 功能
wasmsplit-v2-ci 目前提供以下 9 个对外命令:
init:初始化分包dosplit:生成分包getinfo:获取分包信息disable:关闭代码分包switch-md5:切换工程代码分包 md5,使分包后台将其视为新包(重命名 wasm 文件并同步配置)showfuncname:获取已收集函数名reportfuncname:手动上报函数名文件config:查看 / 管理分包相关配置version:查看 CLI 版本
建议使用场景:
- 大版本开发阶段:按新增函数阈值定期生成 Profile 包
- 小版本(代码无修改或无新增函数)或 bugfix 阶段:直接走增量分包生成 Release 包
# 准备工作
# 密钥配置
wasmsplit-v2-ci 的使用需要传入密钥:在 MP 管理后台「开发管理 → 研发工具箱 → 密钥管理 → Wasm 分包 CI 鉴权配置」上传公钥,本地保存私钥,命令行通过 -k/--private-key-path 指定私钥文件路径。签名算法为 RSA-PSS-SHA256,私钥不会写入项目目录;注意不要把私钥放在小游戏项目目录下(会被一起上传)。
私钥必须是 PEM 格式的 RSA 私钥:格式不对时命令行会直接报「私钥文件格式无效,请确认是有效的 PEM 格式 RSA 私钥」。
# 命令行调用
# 安装
通过 npm 获取:
npm install -g wasmsplit-v2-ci
# 支持命令
初始化分包:
Usage: wasmsplit-v2-ci init [options]
初始化项目并推进到收集态(upload → 预处理 → 分包 → 下载 → 应用)
Options:
-p, --project-path <path> 项目根目录路径(绝对路径,必填)
-k, --private-key-path <path> 私钥文件路径(必填)
-d, --desc <description> 版本描述(首次初始化必填)
--refer-md5 <md5> 依赖的历史版本 MD5
--no-scan 跳过项目扫描(仅用于高级场景)
--game-js <path> game.js 路径(相对项目根,覆盖自动扫描)
--game-json <path> game.json 路径(相对项目根,覆盖自动扫描)
--wasm-code <path> 主包 wasm 文件路径(相对项目根,覆盖自动扫描)
--wx-game-kit <path> wx-game-kit 目录路径(相对项目根,覆盖自动扫描)
--symbols <path> symbols 文件路径(相对项目根,覆盖自动扫描)
-h, --help display help for command
init 命令会检查当前项目(appid + code_md5)的分包状态,完成必须的前置准备工作。流水线每次执行都需调用 init 命令。
如果本次流水线需要增量分包,则传入供增量参考的游戏包 md5(--refer-md5)。增量分包在项目首次分包时使用;首次之后每次分包沿用首次选定的参考版本,不能再更换。
--game-js / --game-json / --wasm-code / --wx-game-kit / --symbols 用于工程结构与默认扫描规则不同时手工指定各项路径;--no-scan 会跳过项目扫描,此时必须自行提供上述路径。
获取分包信息:
Usage: wasmsplit-v2-ci getinfo [options]
Options:
-p, --project-path <path> 项目根目录路径(支持绝对或相对路径,必填)
-k, --private-key-path <path> 私钥文件路径(必填)
-o, --output <path> 自定义输出文件路径
-h, --help display help for command
getinfo 获取信息并以 JSON 格式保存(默认写入 <小游戏项目>/.plugincache/codesplitv2/gameinfo.txt,可用 -o 指定路径)。需要在「分包收集」状态下执行。导出字段:
| 键 | 说明 |
|---|---|
| profileIncrementFuncNum | 新增函数个数 |
| lastCollectFuncTime | 最近一次收到收集上报的时间 |
| sourceFuncNum | 总包函数量 |
| profileFuncNum | 首包函数 |
| isProfile | 当前分包类型(Profile 或 Release) |
| subVersion | 当前分包版本 |
生成分包:
Usage: wasmsplit-v2-ci dosplit [options]
Options:
-p, --project-path <path> 项目根目录路径(支持绝对或相对路径,必填)
-k, --private-key-path <path> 私钥文件路径(必填)
--release Release 分包(默认 Profile 分包)
--refer-md5 <md5> 依赖的历史版本 MD5
--opt-profile Profile 包性能优化
--logCallInWasm Profile 包性能优化(别名)
--opt-func 函数量优化 (Beta)
--mergeSmallFunc 函数量优化 (Beta)(别名)
-h, --help display help for command
有新增函数即可调用,命令执行成功后会下载分包结果并应用到项目目录,执行失败可重复执行。两个优化项开关与插件界面上的勾选项一一对应(见「微信优化项」)。
关闭代码分包:
Usage: wasmsplit-v2-ci disable [options]
Options:
-p, --project-path <projectPath> 小游戏项目路径, 必填
-h, --help display help for command
disable 命令用于关闭代码分包,和插件的关闭代码分包作用相同。该命令的别名是 restore,两个名字等价。
切换分包 md5:
Usage: wasmsplit-v2-ci switch-md5 [options]
Options:
-p, --project-path <projectPath> 小游戏项目路径, 必填
-m, --md5 <md5> 新 md5(15 位 hex,缺省随机生成)
-d, --desc <desc> 切换原因描述(写入 md5-history.csv,仅作记录)
-h, --help display help for command
分包后台按 md5 归档预处理与分包结果:同一个 md5 再次 init 时,工具会查到历史记录并直接复用,跳过上传 / 预处理 / 分包。用 switch-md5 把工程切到一个新的 md5,可以让这次分包被后台当作一个全新的包,从而跑完整链路(例如流水线首次接入时的全链路验证)。该命令只改工程内文件与配置,不访问后台。
只能在未分包状态下使用:检测到子包目录(wasmcode1/、wasmcode2/)、分包运行时脚本 wasm-split.js,或配置里显示分包版本大于 0、分包已锁定时,命令会被拒绝。分包状态下主包与子包共用同一个 md5,只改主包会让两者不一致。需要切换时先关闭代码分包。
获取 / 上报函数名:
Usage: wasmsplit-v2-ci showfuncname [options]
Options:
-p, --project-path <path> 项目根目录路径(绝对路径,必填)
-o, --output <dir> 输出目录路径(默认 <workspace>/.plugincache/codesplitv2/<appId>_<md5>_<subVersion>/)
-k, --private-key-path <path> 私钥文件路径(必填)
Usage: wasmsplit-v2-ci reportfuncname [options]
Options:
-p, --project-path <projectPath> 小游戏项目路径
-k, --private-key-path <privateKeyPath> 私钥文件路径
-r, --report-file-path <reportFilePath> 函数名称列表文件路径
showfuncname 下载首包函数名和新增函数的函数名(分别写入 _primary.txt 与 _increment.txt);reportfuncname 选择之前下载的函数名文件上报(对应插件的「获取首包和缺失函数」与「通过文件上报缺失函数」),方便在切换 appid 后也能实现增量分包,或预先准备好函数名文件来节约收集时间。上报文件是纯文本 UTF-8,每行一个函数名,空行忽略。
其他辅助命令:
Usage: wasmsplit-v2-ci config [options] [command]
Commands:
get [options] <key> 获取配置项
set [options] <key> <value> 设置配置项
list [options] 列出所有配置
Usage: wasmsplit-v2-ci version [options]
Options:
--verbose 显示详细版本信息 (default: false)
# 流水线示例流程
# 注意事项
- 此工具不能完成真机收集过程,真机收集仍需开发者单独执行。
- 分包工具 CI 不提供预览及上传功能,可以使用微信开发者工具 CI 进行预览。
- 请不要将密钥配置文件放在小游戏项目下,这会导致密钥文件一同被上传。
- 如需更改 appid,自行修改项目路径下
project.config.json文件中的 appid。 init命令是幂等的,可缓存中间产物到项目下.plugincache/codesplitv2/目录以加速后续执行。- 支持通过
HTTP_PROXY/HTTPS_PROXY环境变量配置代理。 - 日志文件保存在项目缓存目录
.plugincache/codesplitv2/logs/wasm-split.log,时间戳为本地时间;超过上限(默认 5MB,插件在工程配置里写的是 10MB)时滚动为同目录的.bak,只保留最近一份。
# CI 常见问题排查
- 错误码
-10000401签名验证失败(verify signature error):后台通过响应头logicret通告,说明密钥不匹配或签名不对,请检查私钥与 MP 后台配置的公钥是否配对。 - 私钥文件格式无效:命令行会直接提示「私钥文件格式无效,请确认是有效的 PEM 格式 RSA 私钥」,换成 PEM 格式的 RSA 私钥即可。
- 使用 CI 分包后,
miniprogram-ci上传时报错main package source size ... exceed max limit 4096KB:需要更新miniprogram-ci到对应版本。
# 排查 / 反馈分包问题
分包运行时问题排查时,需要重点关注用户设备的运行环境,在遇到问题时应该确保预先排查:
- 问题形式:问题的发生形式,例如启动失败、加载失败、运行时错误等。
- 问题时刻:问题发生在哪个阶段,例如启动、加载、运行等。
- 问题特征:问题发生与什么环境有关,例如设备类型、系统版本、网络环境等。
- 是否复现:问题是否可以复现,用户遇到的量级有多少,例如是否是随机事件、是否是用户操作导致的。
开发者自身不进行预先排查的,小游戏技术支持很难帮助开发者排查问题。当开发者仍然无法定位和修复问题的时候,可以联系小游戏技术支持,必要时提供游戏导出的项目文件协助排查。
基础库版本和设备信息见vConsole或者日志如下:
分包插件及相关信息见如下:
分包流程运行步骤见如下:
除了直接看vconsole,还可以导出日志文件,然后分析日志文件。
# 运行时常见问题排查
- config 的 fileMD5 与磁盘 wasmcode 目录文件名前缀不一致:会导致子包下载成功但 wasm 实例化失败。请检查
config.json的launch.code.fileMD5与wasmcode*/目录下文件名的前缀是否一致,不一致时需重跑完整分包流程。 - 开发者工具 loadSubpackage 零回调 / readFile 40MB 截断:属开发者工具侧的生态级问题(真机通常无此问题),可在开发者工具中重试,或切换真机验证。
# FAQ
# 分包是否是必要的
对于 iOS 高性能模式,由于内存限制,游戏加载完整 wasm 基本就会内存 crash。分包能降低内存占用,同时我们对子包支持按需加载,才让游戏能稳定跑起来。所以如果是使用了 iOS 高性能模式,分包则是必须的。
对于 android 和 iOS 普通模式,分包主要目的是优化启动加载。这两个运行环境下子包可以全量加载,因此对游戏运行的影响最多是加载子包的一次性开销。iOS 高性能模式下,运行时会按需加载子包,并在启动 callmain 完成后延时 30 秒调度 wasmcode2 的下载,这个时间之后才触发未收集函数的情况,不会有加载子包的影响。
# 收集到什么时候可以结束
按照经验,首包函数占整包个数的比例达到 33% 以上时就可以接受了(参考「评估分包状态」一节)。这不代表没收集的函数都没用了,实际上有些函数可能只是调用比较冷门,后续还是可能被调到,可以通过分包插件面板的「新增函数」来留意线上新增的情况。如果线上新增较多,可以考虑再往下分一次包然后提审发布。
# 游戏内容难以遍历完整怎么办
我们针对这种情况也有线上 Patch 可以进行补漏,但是 Patch 有大小限制。建议新增函数超过一定阈值(参考「评估分包状态」一节)的,还是要生成新分包 + 发版本。
# 分包总大小比原始包大 / 代码包分包后膨胀了很多怎么办
这是正常现象。分包需要一些辅助文件,但只有首包(wasmcode)一定会加载,其他的则会延迟或者按需下载,对运行几乎没有影响。因此不需要关注分包后所有 wasm 包的总和,主要关注 wasm 首包(wasmcode)的大小即可。
# 新增收集的函数要重新再次提审才会在首包吗
对的,用户下载的代码包只能是提审发布过的。
# Profile 分包可以上线吗
不可以。性能有非常大的劣化,在内存和帧率上都表现很差,玩家体验很差。请不要尝试上线 Profile 分包。
# 增量分包没生效怎么办
请检查指定参考的旧版本以及当前版本是否都有 symbol 文件,以及是否有更换引擎或者其他导致代码变动较大的操作。
# 如何更新分包插件
参见「插件更新」章节。
# 分包插件卡住怎么办
遇到流程卡住的问题,一般先尝试关闭分包,然后重启开发者工具,再打开分包,大部分时候有奇效。
# 为什么预处理 / 分包时间特别慢
可能是同一个 appid 提交过一次预处理,正在排队。请确保一次预处理结束之后再进行其他分包操作。分包的时候,如果开发者还在同步收集函数,分包过程会重试以将该阶段收集的函数均纳入,若开发者一直在分包时收集函数,会一直重试直到收集完成,过程可能较长。
# 分包下载经常出错 / 分包经常中断
检查本地网络状态。
# 为什么这次我分包之后多出来了一个 wasmcode2
在新的分包技术版本中,我们为 iOS 引入了 wasmcode2 二级子包,按函数加载缺失的函数,提升用户的运行时体验以及优化开发者的函数收集效率。因此,游戏的代码包空间会有一些膨胀,且游戏运行时内存会增加约 10MB 左右。wasmcode2 与 wasmcode1 的加载逻辑不同,wasmcode2 在 iOS 上还是函数粒度按需加载,wasmcode1 在安卓上是完整加载,这样保证了 iOS 上内存变化基本无感。
# 会不会最终跑到所有函数都收集的情况
目前还没出现这种情况,超过整包 50% 的都很少。大部分游戏收集 1 小时的函数个数都在 33% 到 50% 之间。可以等收集函数超过 75% 了再来考虑这个问题。
# 没有看到增量分包的界面
增量分包需要选择历史版本作为参考(referMd5),如果看不到对应的选项,请确认:是否已经更新到最新的分包插件;以及参考版本与当前版本是否都提供了 symbol 文件。增量分包是后续新增的功能,较早的分包项目可能无法用于增量分包。
# 分包插件安装失败
首先确认是否为 stable 版微信开发者工具(「小游戏版」开发者工具上市场插件可能无法正常更新或安装)。安装入口参见「插件安装」章节;离线或内网环境可把 VSIX 文件拖进「扩展」视图安装。
# 如何查看分包插件日志
插件自身会把每一步命令与状态推断写进小游戏项目里的日志文件:
<小游戏项目>/.plugincache/codesplitv2/logs/wasm-split.log
时间戳为本地时间,超过上限(默认 5MB,插件在工程配置里写的是 10MB)滚动为同目录的 wasm-split.log.bak,只保留最近一份。上传 / 预处理 / 分包 / 下载 / 应用 卡在哪一步、后台返回了什么,基本都能在这里看到。
当出现问题时,优先排查是否 stable 版微信开发者工具,能解决大部分问题。若问题仍无法解决,可联系我们提供日志排查。
扩展宿主层面的日志获取方式:菜单栏 - 微信开发者工具 - 调试 - 调试微信开发者工具,搜索关键字 extension host,可右键保存日志文件。
# 运行时过程
# iOS 高性能模式收集很卡
iOS 高性能模式由于加载子包的实现不同,刚开始收集时又基本是跑子包函数,所以最开始的收集会比较卡。这个时候可以观察分包插件面板,如果能看到新增函数个数的变化,一般就是没问题的。如果出现卡顿(并且有新增函数)或者新增函数较多,可以先继续往下生成分包,再进行收集。游戏运行会随着收集越来越流畅。
# iOS 高性能模式代码分包后内存反而变得很高
这种情况一般是太多新增函数,iOS 高性能模式的子包代码也会占用大量内存,可以继续生成分包,将这部分函数放在首包(放首包的内存占用相对小些)。
# 微信开发者工具无法运行 / Wasm 编译卡住
如果出现以下问题:
- Wasm 编译卡住:
still waiting runDependencies, ids= ["wasm-instantiate"]这类错误,可能是因为 wasm 包过大,真机没有这个问题。 - Wasm 特性不支持:
WebAssembly instantiate(): unexpected section <Exception>这类错误,是因为开发者工具的 wasm 虚拟机暂时不支持。
如果需要模拟器调试请安装2.0x以上的微信开发者工具的Electron 版本。
# 分包后程序无法运行
分包过程中会反复触发 wasm 包下载/更新,开启/关闭分包的时候也会应用/恢复项目包的关键代码。如果在这个更新过程中对项目文件进行操作,可能触发编辑不完全/重复/冲突等情况。一些可能的分包冲突操作如下:
- wasmcode 中的文件名和 game.js 中的 code md5 指定不一致。
- wasmcode 文件夹中存在多个 wasm.br 文件。
- 开发者工具模拟器无法运行:请确认真机是否可以运行。iOS、安卓以及模拟器是三种实现方式,在少部分接口上有差异。
- 分包后缓存没有及时更新,可以清除模拟器缓存(如下图)。
一个可能的分包错误如下:
# iOS 高性能模式出现 import section's count is too big
新版的插件已经规避这个问题,更新插件即可。
# iOS15.4 报错:或偶发白屏闪退、内存不足闪退
iOS 15.4(及 15.4.1)存在一个严重的 WebKit 内核 Regression 漏洞,导致 Safari 浏览器和微信 iOS 客户端在解析和编译大型 WebAssembly (Wasm) 文件时,JIT 引擎(或流式编译器)会出现偶发性的内存对齐与堆栈损坏错误(如报出 Out of bounds memory access、corrupted address 0 或 function not found 等错误)。与代码特征有关,并不是所有 Wasm 都会出现。
可能现象:
[warn] Could not allocate memory: System out of memory!
Trying to allocate: 3196395193B with 16 alignment. MemoryLabel: TempOverflow
Allocation happened at: Line:xxx
规避思路是定位到具体出错的函数,并让编译器不对该函数做优化。Unity 引擎通过条件编译在关键函数上加 #pragma clang optimize off(函数结束时恢复为 on);其他引擎请按各自编译工具链的等价手段处理。
# WebAssembly 相关接口不存在
WebAssembly 相关接口不存在(如 WebAssembly.instantiate):用户的 iOS 可能启用了「锁定模式」,因此该接口被锁定。参考 https://support.apple.com/zh-cn/105120 。