微信小游戏运行在类小程序的宿主环境中,没有 DOM、没有 BOM,只有 Canvas 和一套微信 API。它既不是纯 H5,也不是原生 App,而是一个双线程 + 开放数据域的特殊运行时。专业开发的核心,是理解这套约束并据此设计架构。
微信小游戏采用双线程模型:
game.js,运行 JS 引擎(V8/JavaScriptCore),处理游戏逻辑。wx.createCanvas() 拿到上屏 Canvas。wx.getOpenDataContext(),用于排行榜等社交数据,不能与主域直接通信,只能通过 postMessage。项目最小结构:
game.js # 入口
game.json # 配置
project.config.jsongame.json 示例:
{
"deviceOrientation": "portrait",
"showStatusBar": false,
"networkTimeout": { "request": 5000 },
"subpackages": [
{ "name": "stage2", "root": "stage2/" }
]
}微信小游戏没有 requestAnimationFrame 的 DOM 版本,但 Canvas 提供了 requestAnimationFrame。
// game.js
const canvas = wx.createCanvas();
const ctx = canvas.getContext('2d');
const { windowWidth: W, windowHeight: H } = wx.getSystemInfoSync();
canvas.width = W; canvas.height = H;
const player = { x: W / 2, y: H * 0.8, r: 24, speed: 8 };
let touchX = player.x;
// 触摸事件:小游戏用 wx.onTouchMove,而非 DOM 事件
wx.onTouchMove(e => {
touchX = e.touches[0].clientX;
});
let last = 0;
function loop(ts) {
const dt = Math.min((ts - last) / 1000, 0.05); // 限制最大步长
last = ts;
// 更新:简单跟随
player.x += (touchX - player.x) * Math.min(dt * player.speed, 1);
// 渲染
ctx.clearRect(0, 0, W, H);
ctx.fillStyle = '#0B1D2A';
ctx.fillRect(0, 0, W, H);
ctx.beginPath();
ctx.arc(player.x, player.y, player.r, 0, Math.PI * 2);
ctx.fillStyle = '#C9A96A';
ctx.fill();
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);关键点:用 dt 驱动更新,而非帧计数,才能在不同机型上保持一致速度。
小游戏最怕频繁 GC 和大量 draw call。对象池是标配:
class Pool {
constructor(factory, size = 100) {
this.factory = factory;
this.free = [];
this.active = [];
for (let i = 0; i < size; i++) this.free.push(factory());
}
get() {
const obj = this.free.pop() || this.factory();
this.active.push(obj);
return obj;
}
release(obj) {
const i = this.active.indexOf(obj);
if (i >= 0) { this.active.splice(i, 1); this.free.push(obj); }
}
}
const bulletPool = new Pool(() => ({ x: 0, y: 0, vx: 0, vy: 0, alive: false }), 200);渲染层面,同一纹理的精灵应尽量合批。使用 wx.createImage() 加载纹理,避免逐帧 drawImage 切换图片:
function loadImage(src) {
return new Promise((resolve, reject) => {
const img = wx.createImage();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = src;
});
}小游戏首包限制 4MB,总包 20MB。用分包加载后续关卡:
const task = wx.loadSubpackage({
name: 'stage2',
success: () => console.log('stage2 loaded'),
fail: err => console.error(err),
});
task.onProgressUpdate(res => {
console.log(`加载进度:${res.progress}%`);
});启动优化要点:game.js 只做最必要的初始化,把资源加载放进 wx.onShow 之后的异步流程,避免阻塞首帧。
主域发消息,开放数据域接收并绘制:
// 主域
const odc = wx.getOpenDataContext();
odc.postMessage({ type: 'updateScore', score: 1280 });
// open-data 域(独立文件)
wx.onMessage(msg => {
if (msg.type === 'updateScore') {
wx.setUserCloudStorage({
KVDataList: [{ key: 'score', value: String(msg.score) }],
});
}
});注意:开放数据域不能直接访问主域变量,所有数据必须序列化传递。
wx.getSystemInfoSync() 的 safeArea 避开刘海与底部横条。img.src = '' 释放,监听 wx.onMemoryWarning。wx.onHide 暂停主循环与音频,wx.onShow 恢复,避免后台耗电。wx.onError 与 wx.onUnhandledRejection 上报到自建日志。wx.setEnableDebug({ enableDebug: true }) 观察 FPS 与内存。微信小游戏的专业开发,本质是在“无 DOM、双线程、包体受限”的约束下做架构取舍:用 dt 驱动逻辑、用对象池控制 GC、用分包控制首包、用开放数据域做社交。把这些约束内化为设计习惯,才能做出既流畅又稳定的产品。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。