14.2 微信小游戏构建
微信小游戏本质上是 H5 的裁剪形态:同样跑在 JS 引擎上,但运行环境被微信接管,很多浏览器 API 被替换成微信自己的 wx.* 接口,包体积也被严格限制。14.1 那套 H5 产物直接丢进微信是跑不起来的,必须走「WeChat Mini Game」这条专门的构建链路。
这一节承接 14.1 的工程配置,把微信端的差异讲清楚:构建面板多了哪些必填项、产物里多了哪些微信专属文件、存档与登录为什么要换成 wx.* 接口,以及主包超限这个微信独有的难题怎么解。
14.2.1 前置准备
在动构建之前,有三样东西要先备齐:
- 小游戏 AppID:在微信公众平台注册「小游戏」类型账号,拿到形如
wx0000000000000000的 AppID。个人主体也能注册,但部分能力(如虚拟支付)受限。 - 微信开发者工具:从微信官方下载安装。Cocos 构建时会调用它的命令行来打包预览,所以要在构建面板里把工具安装路径配好。
- 类目与资质:游戏类目需要在微信侧完成类目选择,涉及版号的场景还要提交资质。这一块属于发布流程,本节只做构建,发布资质在 18.1 的检查清单里提。
准备齐全之后,构建面板里会出现「微信小游戏开发者工具路径」这一项,把它指向开发者工具的安装目录。没配的话构建能过,但点「构建并打开」时会失败。
如果你的账号还没拿到 AppID,也可以先留空、只做构建与本地导入验证——但真机预览与上传体验版都要求有真实 AppID,所以尽早去注册,别等到要联调时才卡在账号上。
14.2.2 构建面板取值
在编辑器里打开构建面板:菜单「项目 → 构建发布」→ 新建构建任务 → 平台选 WeChat Mini Game → 填写参数 → 构建。
《方块冲刺》的微信端发布配置如下:
| 选项 | 取值 | 理由 |
|---|---|---|
| 平台 | WeChat Mini Game | 微信小游戏 |
| 发布路径 | build/ | 产物落在 build/wechatgame/ |
| 初始场景 | scenes/boot | 与 H5 一致,启动场景只放加载逻辑 |
| AppID | wx0000000000000000 | 微信公众平台申请 |
| 主包压缩类型 | Merge Deps | 与 H5 一致 |
| MD5 Cache | 勾选 | 与 H5 一致 |
| 分离引擎 | 不勾选 | 引擎与业务打在一起,首版更简单 |
| 远程服务器地址 | https://cdn.example.com/block-dash/ | 关卡分包走 CDN(15.2 讲) |
| 屏幕方向 | 横屏 landscape | 与 960 × 540 设计分辨率一致 |
「分离引擎」是微信端特有的选项:勾选后引擎代码会被单独抽出来,多个小游戏共用一份引擎缓存,能减小下载量;代价是首次进入要额外拉一次引擎。首版我们先不勾,等 15.1 做分包优化时再评估。
「远程服务器地址」在微信端几乎是必填项——因为主包体积有限,关卡资源必须放到远端。注意这个地址必须以 https:// 开头,并在微信公众平台把域名加入「request 合法域名」白名单,否则运行时会直接报「不在以下 request 合法域名列表中」。这个域名白名单是微信端最常见的一类「本地好好的、真机跑不了」问题。
14.2.3 产物结构
构建完成后,build/wechatgame/ 下的结构大致如下:
build/wechatgame/
├── game.js # 微信小游戏入口(被 game.json 指定)
├── game.json # 小游戏运行配置(分包、屏幕方向等)
├── project.config.json # 微信开发者工具的项目配置
├── application.js # 引擎启动器
├── src/
│ ├── settings.json
│ ├── chunks/
│ └── cocos-js/cc.js
└── assets/
├── main/
├── internal/
└── bundle-levels/
和 H5 产物对比,微信端多了三个文件:game.js、game.json、project.config.json,少了 index.html 与 style.css。这是微信运行时的要求——它不用 HTML 承载 canvas,而是直接执行 game.js。其余目录结构与 H5 一致,所以 14.1 里讲的资源组织方式在这里同样成立。
game.json 里的 subpackages 字段决定了哪些资源属于分包。如果 15.1 把 bundle-levels 配成了分包,这里会看到对应的条目;没配的话所有资源都在主包,主包体积就会顶到上限。
还有一点和 H5 不同:微信小游戏的首包下载是串行的,微信先下主包、再按需下分包,而 H5 里浏览器可以并行下载多个文件。这意味着微信端「把资源塞进主包」换来的并发优势并不存在,反而拖慢首包;把非首屏资源拆到分包、让主包尽可能小,才是微信端的正解。这条差异会在 15.1 和 15.3 反复出现。
14.2.4 用微信开发者工具打开与预览
构建完有两种打开方式。一种是构建面板上点「构建并打开」,工具会自动拉起;另一种是手动打开微信开发者工具 → 「导入项目」→ 目录选 build/wechatgame/ → 填入 AppID。
导入后在开发者工具里能看到什么:模拟器窗口里是横屏的小游戏画面,左下角「编译」按钮旁显示当前包体积;工具栏有「预览」「真机调试」「详情」几个按钮。点「详情」能展开项目配置面板,里面有本地设置(是否校验合法域名、是否开启调试)与项目配置(AppID、项目名)。
调试期建议在「详情 → 本地设置」里临时关闭「不校验合法域名」的勾选(即允许不校验),这样还没配白名单的 CDN 地址也能调试;但正式发布前必须把域名配好并重新打开校验,否则线上会请求失败。这个开关是开发者工具本地的,不会随包发布。
真机预览要点「预览」,会生成一个二维码,用微信扫码就能在手机上跑。这一步很重要——开发者工具的模拟器和真机行为有差异,尤其是触摸事件、音频播放和性能表现,13.3 讲的帧预算必须真机复测。
调试完准备给测试同学体验时,点工具栏的「上传」,填版本号与备注,产物会传到微信公众平台的「版本管理」里,在那里可以设为体验版。体验版不需要经过审核,但只对项目成员与体验成员可见;正式发布要提交审核,这一步在 18.1 的发布清单里。注意每次上传的版本号要递增,否则平台会拒绝覆盖。
开发者工具还自带一个「性能」面板,可以看 FPS 与内存曲线,和 13.3 的 Profiler 互补。不过它跑在桌面环境,数值只作参考,真机性能仍以真机 Profiler 为准。
14.2.5 L2:wx 存档与登录桥接
微信端不能用 localStorage,要用 wx.setStorageSync / wx.getStorageSync;登录也要换成 wx.login 换 code 再交给服务端换 openid。下面这段组件把两端差异收在一处,上层业务只调 save / load / login,不用关心当前是哪个平台。它只依赖 cc 的公开 API,能被类型检查。
import { _decorator, Component, sys } from 'cc';
const { ccclass } = _decorator;
declare const wx: {
setStorageSync(key: string, data: string): void;
getStorageSync(key: string): string;
login(opts: {
success(res: { code: string }): void;
fail(err: unknown): void;
}): void;
};
@ccclass('WechatBridge')
export class WechatBridge extends Component {
get isWechat(): boolean {
return sys.platform === sys.Platform.WECHAT_GAME;
}
save(key: string, value: string): void {
if (this.isWechat) {
wx.setStorageSync(key, value);
return;
}
sys.localStorage.setItem(key, value);
}
load(key: string): string {
if (this.isWechat) {
return wx.getStorageSync(key) ?? '';
}
return sys.localStorage.getItem(key) ?? '';
}
login(onCode: (code: string) => void): void {
if (!this.isWechat) return;
wx.login({
success: (res) => onCode(res.code),
fail: (err) => console.error('[wx] login failed', err),
});
}
}
三点说明。第一,wx 不是 cc 的导出,也不在引擎类型声明里,所以这里用 declare const wx 自己声明最小接口——只声明用到的三个方法,避免把整个 wx 类型引入。第二,sys.platform === sys.Platform.WECHAT_GAME 是判断微信小游戏的唯一正确方式,别用 navigator.userAgent。第三,wx.login 拿到的 code 只能换一次 openid,必须发给服务端去换(服务端接口在卷四讲),客户端拿到 code 后不要缓存。
原生端(14.3)的存档用 sys.localStorage,与 H5 共用一条分支;所以这个桥接类实际覆盖了 H5、微信、原生三端中的两套逻辑。
存档键也要三端统一。按 §C 的工程约定,《方块冲刺》的存档键固定为 blockdash.save.v1,三端用的是同一个键名——这样用户从 H5 转到小游戏时,存档迁移逻辑(11.1 讲的那套版本迁移)不用改。键名里的 v1 是存档格式版本,不是应用版本,升级格式时递增它、并在迁移函数里处理旧格式。
14.2.6 L3:微信专属工程文件
下面是构建产出的 game.json 片段。本段是 Cocos Creator 工程文件,需 Cocos Creator 3.8 编辑器打开,本节不可命令行复现。
{
"deviceOrientation": "landscape",
"showStatusBar": false,
"networkTimeout": {
"request": 10000,
"downloadFile": 10000
},
"subpackages": [
{ "name": "bundle-levels", "root": "assets/bundle-levels/" }
]
}
在编辑器里打开构建面板并切到微信平台,你会看到什么:屏幕方向 是一个下拉,当前显示 landscape;主包压缩类型 显示 Merge Deps;远程服务器地址 显示 https://cdn.example.com/block-dash/;MD5 Cache 已勾选。构建后生成的 game.json 里,subpackages 数组会自动填入 15.1 配置的分包名与路径。
(以上工程文件需 Cocos Creator 3.8 编辑器打开,本节不可命令行复现。)
再看 project.config.json 片段。本段同样是 Cocos Creator 工程文件,需 Cocos Creator 3.8 编辑器打开,本节不可命令行复现。
{
"appid": "wx0000000000000000",
"projectname": "block-dash",
"setting": {
"urlCheck": true,
"es6": false,
"minified": true
}
}
在微信开发者工具里点「详情」,你会看到什么:项目配置页的 AppID 与上面一致,项目名称 是 block-dash;本地设置页里 ES6 转 ES5 关闭(因为 Cocos 已经转译过)、代码压缩 打开、不校验合法域名 关闭。urlCheck: true 对应「校验合法域名」,正式包必须为 true。
这三个文件(game.js / game.json / project.config.json)是构建生成的,不要手动改——下次构建会覆盖。要改配置,去 Cocos 的构建面板或微信公众平台改。
换句话说,微信端的「源码」在 Cocos 工程里,build/wechatgame/ 只是它的编译产物,和 build/web-mobile/ 一样不该进版本库。
(以上工程文件需 Cocos Creator 3.8 编辑器打开,本节不可命令行复现。)
14.2.7 主包体积与常见报错
微信小游戏最硬的约束是主包体积上限。微信官方长期把主包上限设在 4 MB,分包可以把总容量扩到几十 MB——具体数值以微信官方最新文档为准。Cocos 产物的主包 = 引擎(cocos-js/cc.js)+ 业务脚本 + 主包资源,其中引擎就占了 1 MB 出头,所以留给资源的空间其实很紧张。
主包瘦身的顺序建议:
- 把大图压缩(13.2 的压缩预设,主包资源必须压);
- 把非首屏资源拆到分包(15.1);
- 勾选「分离引擎」(若引擎复用收益大于额外下载);
- 删掉没用到的引擎模块(构建面板的「引擎裁剪」)。
「引擎裁剪」是 Cocos 提供的一项能力:在构建面板勾选「按需裁剪引擎」,引擎只打包你实际用到的模块,没用到的 3D 物理、骨骼动画、Spine 等会被剔除。对一个纯 2D 跑酷来说,这一刀通常能砍掉几百 KB 的引擎体积,是主包瘦身里性价比最高的一步。裁剪的代价是「用到了没勾的模块」会在运行时才报错,所以裁剪后一定要把每个场景都点一遍。
常见报错与处置:
| 报错 / 现象 | 原因 | 处置 |
|---|---|---|
| 主包超过 4 MB | 资源全在主包 | 拆分包、压纹理、裁引擎 |
| request 不在合法域名列表 | CDN 域名未加白名单 | 微信公众平台配 request 域名 |
| 真机白屏、模拟器正常 | 分包加载路径或域名问题 | 看真机 vConsole 报错 |
| 音频不播放 | 未在用户手势后播放 | 首次播放绑到点击回调 |
wx is not defined | 在非微信环境调了 wx | 先判 sys.platform 再调 |
最后一条正是 14.2.5 那段桥接代码要解决的问题:所有 wx.* 调用前都必须先判平台,否则在 H5 或编辑器预览里直接抛 wx is not defined。把这类平台差异全部收进 assets/scripts/platform/ 一个目录,是让「三端同一份代码」保持可维护的关键——业务层永远不直接碰 wx。
14.2.8 收口
做完微信小游戏构建,工程里多了这些:
build/wechatgame/一份可导入微信开发者工具的产物;settings/v2/packages/builder.json里的wechatgame构建任务配置;assets/scripts/platform/WechatBridge.ts一个跨端存档与登录桥接组件;- 一份域名白名单与主包体积的检查项。
验收标准是:产物导入微信开发者工具能编译通过,模拟器与真机都能进入 main 场景,存档能在关闭后重新打开时读回,主包体积在限制以内。
上线前逐条走一遍:
- AppID 已填入,且与小游戏账号一致;
- 远程服务器地址是
https://,且域名已加 request 白名单; - 主包体积在 4 MB 以内(以微信官方文档为准),超了先拆分包再压纹理;
-
game.json的subpackages已包含bundle-levels; - 所有
wx.*调用前都判了sys.platform === sys.Platform.WECHAT_GAME; - 真机预览通过,音频与触摸行为正常。
下一节把同一份工程构建成原生 Android / iOS——那一步要生成真正的原生工程,涉及包名、签名、SDK 版本这些移动开发的老朋友。
阅读导航:上一节:14.1 H5 构建与部署 · 下一节:14.3 原生 Android / iOS 构建 。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。