微信小游戏开发

用生活化的比喻,让你掌握微信小游戏的架构差异、关键 API 和优化策略

前置知识:JavaScript 基础、4_1_game-architecture(游戏架构)


阅读指南(初学者必看)

为什么你需要学习微信小游戏开发?

微信小游戏是国内最大的 H5 游戏分发平台,月活用户超过 10 亿。如果你要在中国市场做 H5 游戏,微信小游戏几乎是必经之路。但微信小游戏和普通 H5 有很多差异——没有 DOM、首包 4MB 限制、特殊的 API 体系。理解这些差异,是开发小游戏的第一步。

学完本章,你能回答:

  • 微信小游戏和普通 H5 有什么本质区别?
  • 微信小游戏的关键 API 怎么使用(登录、分享、支付、广告)?
  • 如何优化包体、内存和启动速度?

本文结构

第一部分:微信小游戏架构(与普通H5的差异)
第二部分:关键 API(登录/分享/支付/广告/开放数据域)
第三部分:小游戏优化(包体/内存/启动)
第四部分:发布上线

一、微信小游戏架构

微信小游戏 vs 普通H5:
- 没有DOM(不能用document/window)
- 没有BOM(用wx.request代替XMLHttpRequest)
- 有微信API(支付/分享/广告)
- 首包限制4MB
- 内存限制(iOS约1GB)

1.1 运行环境对比

生活类比:普通 H5 像是在"自由市场"做生意,什么都能用;微信小游戏像是在"商场"里开店,场地有限,但客流巨大,还有商场提供的支付和推广服务。

维度 普通 H5 微信小游戏
DOM ✅ 完整支持 ❌ 没有
BOM ✅ window/navigator等 ❌ 用 wx API 替代
网络 XMLHttpRequest/Fetch wx.request
存储 localStorage wx.setStorageSync
音频 Web Audio API wx.createInnerAudioContext
Canvas ✅(核心渲染方式)
WebGL
包体大小 无限制 首包 ≤ 4MB,总包 ≤ 20MB
内存 无明确限制 iOS ~1GB,Android 设备各异

1.2 小游戏启动流程

小游戏启动流程:

用户点击 ──▶ 下载首包(<4MB) ──▶ 初始化运行环境
                                      │
                                      ▼
                              加载游戏代码(Canvas渲染)
                                      │
                                      ▼
                              显示首屏(<3秒目标)
                                      │
                                      ▼
                              按需下载子包资源
                                      │
                                      ▼
                              完整游戏体验

关键时间节点:
- 冷启动 → 首屏展示:< 3秒
- 首包下载 → 代码执行:< 1秒
- 子包下载:后台进行,不阻塞游戏

1.3 项目结构

my-game/
├── game.js              # 游戏入口
├── game.json            # 游戏配置
├── project.config.json  # 项目配置
├── images/              # 图片资源
├── audio/               # 音频资源
└── js/                  # JavaScript代码
// game.json
{
  "deviceOrientation": "portrait",
  "showStatusBar": false,
  "networkTimeout": {
    "request": 5000,
    "connectSocket": 5000,
    "uploadFile": 5000,
    "downloadFile": 5000
  }
}
// game.js 入口
const canvas = wx.createCanvas();
const ctx = canvas.getContext('2d');
canvas.width = wx.getSystemInfoSync().windowWidth;
canvas.height = wx.getSystemInfoSync().windowHeight;

function gameLoop() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = '#000';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = '#fff';
  ctx.font = '20px Arial';
  ctx.fillText('Hello WeChat Mini Game', 50, 50);
  requestAnimationFrame(gameLoop);
}
gameLoop();

二、关键 API

2.1 登录

// 登录
wx.login({ success: res => { /* res.code 换 openid */ } });
微信登录流程:
1. 前端调用 wx.login() 获取 code
2. 将 code 发送到你的服务器
3. 服务器用 code + appId + appSecret 向微信服务器换 openid
4. 服务器生成自定义登录态返回前端
5. 前端保存登录态,后续请求带上

┌──────┐    wx.login(code)    ┌──────┐    code+secret    ┌──────┐
│ 前端 │────────────────────▶│ 服务端│───────────────▶│ 微信 │
│      │◀────────────────────│      │◀───────────────│      │
└──────┘   自定义登录态       └──────┘   openid+session  └──────┘

2.2 用户信息

// 创建用户信息按钮(新版API)
let button = wx.createUserInfoButton({
  type: 'text',
  text: '获取用户信息',
  style: {
    left: 100, top: 200, width: 200, height: 40,
    lineHeight: 40, backgroundColor: '#07C160',
    color: '#ffffff', textAlign: 'center',
    fontSize: 16, borderRadius: 4
  }
});
button.onTap((res) => {
  console.log('用户信息', res.userInfo);
  button.destroy();
});

2.3 分享

// 主动分享
wx.shareAppMessage({ title: '来玩!', query: 'inviter=123' });

// 监听右上角分享按钮
wx.onShareAppMessage(() => {
  return { title: '快来玩这个游戏!', imageUrl: 'images/share.png', query: 'from=share&id=123' };
});
分享机制:
- 主动分享:玩家点击分享按钮触发
- 被动分享:游戏结束时"分享复活"等
- 分享到聊天:单聊/群聊
- 分享到朋友圈:仅支持小程序,小游戏暂不支持

关键参数:
- title:分享卡片标题
- query:分享参数(用于追踪来源)
- imageUrl:分享卡片图片(可选自定义)

2.4 支付与广告

// 虚拟支付
wx.requestMidasPayment({ offerId: 'xxx', purchaseQuantity: 10 });

// 激励视频广告
const ad = wx.createRewardedVideoAd({ adUnitId: 'xxx' });
ad.show().then(() => { /* 发放奖励 */ });
小游戏变现模式:

┌──────────────────────────────────────────────┐
│             小游戏变现方式                      │
├──────────────┬───────────────────────────────┤
│  内购(IAP)  │  广告                           │
│  ├─ 虚拟货币  │  ├─ 激励视频(最赚钱)            │
│  ├─ 皮肤/角色 │  ├─ 插屏广告                     │
│  └─ 通行证   │  ├─ Banner广告                  │
│              │  └─ 原生广告                     │
└──────────────┴───────────────────────────────┘

激励视频广告流程:
1. 玩家点击"看广告获得奖励"
2. 加载广告 → 展示30秒视频
3. 玩家看完 → 服务端验证 → 发放奖励
4. 如果广告加载失败 → 降级方案(直接发放或重试)

2.5 本地存储

// 同步存储
wx.setStorageSync('key', 'value');
let value = wx.getStorageSync('key');

// 异步存储
wx.setStorage({ key: 'key', data: 'value', success: () => console.log('存储成功') });
wx.getStorage({ key: 'key', success: (res) => console.log('获取成功', res.data) });

// 删除存储
wx.removeStorageSync('key');
wx.clearStorageSync();

2.6 网络请求

// 发起请求
wx.request({
  url: 'https://api.game.com/user',
  method: 'GET',
  data: { id: 123 },
  success: (res) => console.log('请求成功', res.data),
  fail: (err) => console.error('请求失败', err)
});

// 下载文件
wx.downloadFile({
  url: 'https://game.com/image.png',
  success: (res) => console.log('下载成功', res.tempFilePath)
});

2.7 开放数据域

// 开放数据域(好友排行)
wx.getOpenDataContext().postMessage({ command: 'getFriendRanking' });
开放数据域架构:

┌──────────────────────┐    postMessage    ┌──────────────────────┐
│   主域(游戏逻辑)      │────────────────▶│  开放数据域(好友数据)  │
│   - 不能访问好友数据    │                  │  - 只能访问好友数据     │
│   - 可以渲染Canvas     │◀────────────────│  - 不能联网            │
│   - 可以调用所有API     │   共享Canvas      │  - 只能渲染到共享Canvas │
└──────────────────────┘                   └──────────────────────┘

为什么要有开放数据域?
- 安全:防止游戏获取好友隐私数据传到自己服务器
- 隔离:好友数据只能在开放数据域中使用
- 共享:通过共享Canvas把排行榜渲染结果传给主域

三、小游戏优化

3.1 包体优化

包体优化(首包<4MB):
1. 代码:Tree Shaking + 压缩 + 分包
2. 资源:图片/纹理/音频压缩 + 分包下载
3. 分包策略:首包只放启动+登录,子包按需下载

分包策略示例:
┌──────────────────────────────┐
│  首包(<4MB)                  │
│  ├─ 游戏代码(压缩后)          │
│  ├─ 启动页资源                 │
│  └─ 登录UI资源                 │
├──────────────────────────────┤
│  子包1:主城场景(按需下载)     │
│  ├─ 城市贴图                   │
│  └─ NPC模型                   │
├──────────────────────────────┤
│  子包2:战斗场景(按需下载)     │
│  ├─ 战斗特效                   │
│  └─ 怪物模型                   │
└──────────────────────────────┘
// game.json 分包配置
{
  "subpackages": [
    { "name": "level1", "root": "subpackages/level1/" },
    { "name": "level2", "root": "subpackages/level2/" }
  ]
}
// 加载分包
wx.loadSubpackage({
  name: 'level1',
  success: () => console.log('分包加载成功'),
  fail: (err) => console.error('分包加载失败', err)
});

3.2 内存优化

内存优化:
- 纹理压缩(ETC2/ASTC减少显存)
- 对象池(减少GC)
- 资源释放(切场景时主动释放)
- LRU缓存

纹理压缩对比:
| 格式 | 平台 | 压缩比 | 质量 |
|------|------|--------|------|
| PNG | 通用 | 无压缩 | 完美 |
| JPEG | 通用 | 10:1 | 有损 |
| ETC2 | Android | 4:1 | 有损 |
| ASTC | iOS/部分Android | 4:1~8:1 | 可调 |
| WebP | 通用 | 2:1~3:1 | 有损/无损 |

对象池实现:

class ObjectPool {
  constructor(factory, initialSize = 50) {
    this.factory = factory;
    this.pool = [];
    for (let i = 0; i < initialSize; i++) this.pool.push(factory());
  }
  get() { return this.pool.pop() || this.factory(); }
  release(obj) { this.pool.push(obj); }
}

3.3 启动优化

启动优化:
- 代码预加载
- 首屏<3秒
- 骨架屏

启动优化时间线:
0ms ──── 用户点击 ──── 下载首包 ──── 解析代码 ──── 初始化引擎 ──── 渲染首屏
                                          │                              │
                                     500ms                          3000ms
                                    代码开始执行                     看到画面

优化手段:
1. 减少首包大小(Tree Shaking、代码压缩)
2. 预加载关键资源(启动时就开始下载子包资源)
3. 骨架屏(先显示UI框架,数据异步加载)
4. 延迟初始化(非关键模块在首屏后初始化)

四、发布上线

4.1 版本管理

// 检查更新
const updateManager = wx.getUpdateManager();

updateManager.onCheckForUpdate((res) => {
  console.log('是否有新版本', res.hasUpdate);
});

updateManager.onUpdateReady(() => {
  wx.showModal({
    title: '更新提示',
    content: '新版本已经准备好,是否重启应用?',
    success: (res) => { if (res.confirm) updateManager.applyUpdate(); }
  });
});

updateManager.onUpdateFailed(() => {
  console.error('新版本下载失败');
});

4.2 发布流程

1. 开发调试
   - 使用微信开发者工具
   - 真机调试

2. 上传代码
   - 点击"上传"按钮
   - 填写版本号和备注

3. 提交审核
   - 登录微信公众平台
   - 提交审核(通常3~7天)

4. 发布上线
   - 审核通过后发布

4.3 注意事项

1. 隐私协议
   - 必须提供隐私协议
   - 收集用户信息需要授权

2. 内容审核
   - 不能有违规内容
   - 不能有诱导分享

3. 性能要求
   - 启动时间 < 3秒
   - 内存占用 < 300MB
   - CPU占用 < 50%

4. 兼容性
   - 测试不同机型
   - 测试不同微信版本

实践

  • 发布一个微信小游戏
  • 实现分包加载(首包<4MB)
  • 接入微信支付和广告

自问自答

Q:微信小游戏和微信小程序有什么区别? A:小游戏是小程序的一种特殊类型。小程序有 DOM(WXML/WXSS),小游戏没有 DOM,只能用 Canvas 渲染。小游戏更接近游戏引擎的运行方式,小程序更接近传统 Web 开发。

Q:首包 4MB 够用吗? A:对于纯代码来说够用了,关键是不要把资源放首包。策略:首包只放代码 + 启动必需的最少资源,其余资源放子包或 CDN 按需下载。一个成熟的小游戏首包通常在 2~3MB。

Q:开放数据域为什么要单独一个环境? A:为了隐私安全。如果主域能直接读取好友数据,游戏可以把好友信息发到自己服务器,造成隐私泄露。开放数据域限制了好友数据只能在隔离环境中使用。

Q:激励视频广告和插屏广告哪个更赚钱? A:激励视频。因为玩家主动选择看广告,完播率高,广告主愿意出更高的价格。插屏广告打扰体验,完播率低。最佳实践:把激励视频和游戏机制结合(看广告复活/获得道具),玩家不反感,收入也高。

Q:微信小游戏代码能直接在浏览器运行吗? A:大部分可以,但需要适配:window对象 → wx对象,DOM操作 → Canvas操作,网络请求 → wx.request。建议在开发时先写Web版,再适配到微信小游戏。


实践任务

  • 任务1:注册微信小游戏开发者账号,创建第一个小游戏项目并真机预览
  • 任务2:实现分包加载——首包只放启动代码和登录页,主游戏内容放子包
  • 任务3:接入微信登录流程(前端 wx.login + 后端 code2session)
  • 任务4:接入激励视频广告,实现"看广告获得金币"功能
  • 任务5:优化小游戏启动速度——使用骨架屏 + 延迟初始化,目标冷启动 < 3秒
  • 任务6:接入版本更新管理器,实现热更新提示功能

与其他章节的关联

本章内容 关联章节 关联点
首包优化 第01章 WebAssembly深度 Wasm 二进制体积小,适合首包
内存优化 2_3_browser-memory-mastery 小游戏内存限制更严,优化方法通用
Canvas渲染 2_2_h5-rendering-mastery 小游戏只能用Canvas渲染,GPU优化方法适用
广告/支付 第06章 多平台小游戏适配 不同平台的广告/支付API不同
分包加载 4_1_game-architecture 资源管理和分包策略是架构设计的一部分

上一章:04-图形学前沿 | 下一章:06-多平台小游戏适配