《Cocos 游戏开发实战》14.2 微信小游戏构建

微信小游戏是 H5 的裁剪形态,但限制更多:主包有体积上限、必须配远程服务器地址、还得在微信开发者工具里预览。本节把构建面板上的微信专属选项逐项给出取值,讲清 wechatgame 产物的结构,给出可直接编译的 wx 存档与登录桥接脚本,并整理主包超限与常见报错的处置办法。

14.2 微信小游戏构建

微信小游戏本质上是 H5 的裁剪形态:同样跑在 JS 引擎上,但运行环境被微信接管,很多浏览器 API 被替换成微信自己的 wx.* 接口,包体积也被严格限制。14.1 那套 H5 产物直接丢进微信是跑不起来的,必须走「WeChat Mini Game」这条专门的构建链路。

这一节承接 14.1 的工程配置,把微信端的差异讲清楚:构建面板多了哪些必填项、产物里多了哪些微信专属文件、存档与登录为什么要换成 wx.* 接口,以及主包超限这个微信独有的难题怎么解。

14.2.1 前置准备

在动构建之前,有三样东西要先备齐:

  1. 小游戏 AppID:在微信公众平台注册「小游戏」类型账号,拿到形如 wx0000000000000000 的 AppID。个人主体也能注册,但部分能力(如虚拟支付)受限。
  2. 微信开发者工具:从微信官方下载安装。Cocos 构建时会调用它的命令行来打包预览,所以要在构建面板里把工具安装路径配好。
  3. 类目与资质:游戏类目需要在微信侧完成类目选择,涉及版号的场景还要提交资质。这一块属于发布流程,本节只做构建,发布资质在 18.1 的检查清单里提。

准备齐全之后,构建面板里会出现「微信小游戏开发者工具路径」这一项,把它指向开发者工具的安装目录。没配的话构建能过,但点「构建并打开」时会失败。

如果你的账号还没拿到 AppID,也可以先留空、只做构建与本地导入验证——但真机预览与上传体验版都要求有真实 AppID,所以尽早去注册,别等到要联调时才卡在账号上。

14.2.2 构建面板取值

在编辑器里打开构建面板:菜单「项目 → 构建发布」→ 新建构建任务 → 平台选 WeChat Mini Game → 填写参数 → 构建。

《方块冲刺》的微信端发布配置如下:

选项取值理由
平台WeChat Mini Game微信小游戏
发布路径build/产物落在 build/wechatgame/
初始场景scenes/boot与 H5 一致,启动场景只放加载逻辑
AppIDwx0000000000000000微信公众平台申请
主包压缩类型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 出头,所以留给资源的空间其实很紧张。

主包瘦身的顺序建议:

  1. 把大图压缩(13.2 的压缩预设,主包资源必须压);
  2. 把非首屏资源拆到分包(15.1);
  3. 勾选「分离引擎」(若引擎复用收益大于额外下载);
  4. 删掉没用到的引擎模块(构建面板的「引擎裁剪」)。

「引擎裁剪」是 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 构建 。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「cocos」更多文章

  1. 《Cocos 游戏联机实战》9.3 延迟补偿与体验权衡
  2. 《Cocos 游戏联机实战》9.2 网络状态 UI 与提示
  3. 《Cocos 游戏联机实战》9.1 同步表现与插值动画