小游戏启动还在黑屏?官方封面图插件接入教程

小游戏启动还在黑屏?官方封面图插件接入教程
青萍叙事前言
点开一个小游戏,最劝退的不是玩法,是开头那几秒黑屏。
原因不复杂:首包里塞着游戏引擎和一大堆启动代码,微信得先把它们下载完、注入完、等 WebGL 初始化,屏幕上才画得出第一帧。这段时间内玩家什么也看不到,很多人直接就退了。
社区里流传的最佳实践是「首包只画一张静态图」,不依赖引擎、直接用 WebGL 画,让玩家第一时间看到画面。思路没错,但自己写这套逻辑挺别扭:想加点进度条、多图切换,就得把渲染引擎也塞进首包,包又大回去了。
微信官方把这件事封装成了 封面图插件(MinigameLoading)。这篇就按官方文档把接入流程走一遍,顺带说说几个照着文档抄也容易翻车的地方。
官方文档:封面图插件(最后更新 2026-04-09)
它到底做了什么
一句话:把「等引擎」的时间,换成「先看封面」。
插件把绘制静态图的 WebGL 逻辑封装在内部,渲染引擎由基础库内置、官方维护并静默更新,所以不占用你自己的代码包体积。基于它还能用更丰富的封面模板,官方点名了静态图模板、多图切换模板、进度条模板三类。
插件本身不占包体,但真正让首包变小的,是你把游戏代码拆成分包。官方把改造成本说得很直白:准备一张封面图,更改初始化流程,把游戏代码都改成子包加载,首帧逻辑改成初始化插件并异步加载游戏代码 —— 有图的话,预计 1~2 天接完。
官方给的应用案例是《星途WeGoing》,表现是「封面图 → 游戏 loading → 正式进入游戏」,安卓总启动耗时 2.3s、提升 34%,iOS 1.6s、提升 33%。这是官方案例数据,不是接入后必然拿到的数字,自己项目还是要真机测。
接入前准备
- MP 后台添加插件:登录后台,打开「小游戏封面图」插件页面点「添加插件」。按官方《小游戏插件》文档的说法,通常无需审批,添加后即可使用。申请入口在封面图插件文档的「1. 申请插件」里。
- 一张封面图:放到构建产物目录(如
images/),社区实践一般用 JPG,同尺寸下比 PNG 小不少。 - 基础库 2.1.0 及以上:分包能力从这个版本开始支持,官方示例也用
compareVersion(SDKVersion, '2.1.0')做判断。低版本要做兼容。 - 微信开发者工具 + 真机:封面阶段涉及 GL 上下文,工具里能跑不代表真机没问题。
另外提醒一句:目前小游戏只开放使用官方提供的插件,不能自己发布插件;封面图插件属于官方插件列表里的一员。
五步接入
第一步:申请插件
在 MP 后台打开封面图插件页面点「添加插件」,通常无需审批。这一步不能跳,没添加的话后面 requirePlugin 会直接失败。
第二步:在 game.json 声明插件
1 | { |
几个点:
MinigameLoading是你自己起的引用名,后面requirePlugin要用同一个名字;provider是封面图插件的固定 AppIDwxbd990766293b9dc4,不能改;version: "latest"用最新版,社区实践里也有钉死具体版本(如1.0.22)的写法;contexts: [{ "type": "isolatedContext" }]表示独立域插件,照抄别漏,漏了容易在运行时找不到插件。
第三步:先取 contextType 和 contextAttributes
这一步最容易被跳过,但它是后面不出画面问题的关键。官方要求在游戏画面渲染后执行这段代码,把结果记下来:
1 | let ctx = GameGlobal.canvas.getContext('2d') |
不同引擎、不同项目拿到的 contextAttributes 可能不一样(alpha、antialias、depth、stencil、preserveDrawingBuffer 这些)。用自己项目里打印出来的值,不要从别人博客复制,否则插件创建的 GL 上下文和游戏上下文参数不一致,轻则画面异常,重则冲突黑屏。
第四步:game.js 里渲染封面
官方示例(我按官方结构整理,catch 里的变量名后面会说):
1 | if (compareVersion(wx.getSystemInfoSync().SDKVersion, '2.1.0') > -1) { |
逐项说:
requirePlugin(name, { customEnv })把wx和canvas注入给插件,customEnv里的 canvas 在有些引擎工程里是全局canvas变量,按你的构建产物调整;.default是插件导出的LoadingManager,挂到GameGlobal上是为了后面main.js也能调用;create({ images, contextType, contextAttributes })是渲染封面的入口,images[].src就是封面图相对路径;create()返回 Promise,.then()里封面已经显示,适合在这里启动分包加载;- 基础库判断用的
compareVersion实现见官方版本号比较文档,思路是把版本号按.拆开逐段比大小,补零对齐后再比; - 社区实践里还会传
scaleMode: GameGlobal.LoadingManager.ScaleMode.FIXED_WIDTH控制封面缩放,以及多图、进度条等模板参数 —— 这些属于插件独立 API,完整参数要登录 MP 后台看插件文档,公开文档里只给了最小示例。
这里有个官方文档的小笔误:原文 catch(err) 块里写的是 console.error('当前环境不支持使用插件', error),变量 error 并没有定义,照抄会在捕获异常时再抛一个 ReferenceError。改成 err 就行。
第五步:隐藏封面
游戏首帧就绪后销毁封面:
1 | GameGlobal.LoadingManager.destroy().then(() => { |
destroy() 同样返回 Promise。时机很讲究,尤其封面和游戏共用一个 canvas 时,必须在引擎初始化之前销毁,否则两边抢 GL 上下文。社区里的 Cocos 改造用 LoadingManager.isMainCanvas 区分两种情况:
- 共用主屏:在
cc.game.run()之前destroy().then(() => cc.game.run(...)); - 非共用主屏:在首场景
loadScene的完成回调里destroy()。
真正的关键:把游戏代码拆进分包
只加封面不拆包,等于白干 —— 首包还是那么大,玩家照样得等。
官方《分包加载》文档里的配置方式:
1 | { |
subpackages 里配置的目录或 JS 文件会被打成独立分包,没配置的都进主包。主包上限 4M,所有主包加分包合计不超过 30M,单个普通分包不限大小、独立分包不超过 4M。
触发加载用 wx.loadSubpackage:
1 | const loadTask = wx.loadSubpackage({ |
整体顺序是:首包启动 → 初始化插件并显示封面 → 封面背后 loadSubpackage 异步拉引擎和游戏代码 → 加载完成、引擎跑起来、首帧就绪 → destroy() 销毁封面。
老版本兼容不用自己写两套:微信后台会编译分包版和整包兼容版两份代码包,旧客户端下载整包启动;基础库 2.1.0 以下没有 wx.loadSubpackage,开发者用 require('stage1/game.js') 触发入口加载即可。
官方还给了一个完整改造示例仓库:cocos-blackjack。
Cocos Creator 项目怎么改
如果你用 Cocos Creator 构建微信小游戏,社区里有一份很完整的实操记录(Asroads’Blog,Cocos Creator 2.4.11),核心是改三个文件、加一张图:
| 文件 | 改什么 |
|---|---|
game.json |
在原有 plugins 里加 MinigameLoading 声明(引擎插件如 cocos 保留) |
game.js |
把原来同步 require 引擎的流程收进 loadGame();先 create() 显示封面,再异步加载游戏 |
main.js |
按 isMainCanvas 决定 destroy() 时机:共用主屏在 cc.game.run() 前,否则在 loadScene 回调里 |
images/img_bj.jpg |
新增封面图,放在构建产物目录里 |
game.js 里的 loadGame() 依次 requirePlugin('cocos')、__globalAdapter.adaptEngine()、require('./ccRequire')、require('./src/settings')、require('./main'),最后 window.boot()。也就是说引擎相关的加载全被推到封面显示之后,首包只剩封面逻辑和插件初始化。
顺带说下那份实战里的一个细节:引擎插件声明里带 path: "cocos" 只用于本地调试,正式环境要删掉。
九个容易踩的坑
- 没在 MP 后台添加插件就调
requirePlugin,直接失败。 game.json漏了contexts: [{ "type": "isolatedContext" }],插件加载不起来。contextAttributes照抄别人的。一定要接入前在自己项目里读一次,这是最隐蔽的坑。- **官方示例
catch(err)里写的是error**,照抄会多抛一个 ReferenceError(前面提过)。 - 封面图路径写错或图太大。路径是相对构建产物的,图片本身会进主包,别放几 MB 的原图。
destroy()时机不对。共用主屏必须在引擎初始化前销毁,不然 GL 上下文冲突;太早销毁又会露黑屏。- 只改封面、没拆分包。首包没变小,优化等于没做。
- 低版本基础库没兜底。2.1.0 以下既没有分包也没有插件能力,
else分支要走原来的整包启动流程;极老客户端不支持插件时可能黑屏。 - 平台绑定。这是微信小游戏的能力,抖音、QQ 小游戏等平台不通用,做多端发布时要单独处理。
怎么验证有没有生效
- 微信开发者工具里看首包体积是否明显下降,以及启动流程里是否出现「封面 → loading → 游戏」三段;
- 真机(尤其低端安卓)冷启动,确认第一眼就是封面而不是黑屏;
- 检查封面显示期间分包是否真的在后台下载,别把
loadSubpackage写成了串行等待; - 对比接入前后的启动耗时,也可以配合官方的自定义启动上报做漏斗分析。
写在最后
封面图插件不难接,五步、几段代码,官方文档也就一页。它真正要求你动的是项目结构:把「所有东西塞进首包、按次序同步加载」改成「先给玩家一张图,剩下的后台慢慢来」。
如果你的小游戏首包已经逼近 4M,或者冷启动黑屏被吐槽过,这个改造值得花那一两天。毕竟玩家愿不愿意留下来,往往就在点开后的头两秒。










