Bundle
Bundle 管理:加载 / 预加载 / 场景切换 / 热更 与 管理器基类
前置知识:
介绍#
mk.bundle
封装 bundle 的加载 / 场景加载 / 预加载 / 热更,并提供 bundle/scene 切换事件和「bundle 管理器」机制用于子游戏或模块化管理。
核心能力:
-
加载(load)与预加载(preloadScene)场景;
-
支持远程/本地 bundle、bundle 热更(reload);
-
切换前 / 切换后事件(bundle/scene);
-
提供
BundleManageBase
作为 bundle 级管理器基类,方便注册生命周期、管理对象池、网络与资源释放; -
提供
followRelease
机制,方便将资源/对象绑定到 bundle 生命周期。
属性#
event#
bundle 事件,使用方式参考 此处,提供如下事件
-
beforeBundleSwitch
: bundle 切换前事件 -
afterBundleSwitch
: bundle 切换后事件 -
beforeSceneSwitch
: 场景切换前事件 -
afterSceneSwitch
: 场景切换后事件
bundleMap#
bundle 信息表,用于存储 set
接口的数据
sceneStr#
只读,当前场景名
bundleStr#
只读,当前场景所属 bundle 名
preBundleStr#
只读,上个场景所属 bundle 名
isSwitchScene#
只读,当前是否处于场景切换流程中(loadScene 期间为 true)
方法#
set#
更新 bundle 信息,数据将存储在 bundleMap
中,后续 load
/loadScene
会使用它
参数#
set(bundleInfo)
-
bundleInfo
: bundle 信息 -
bundleInfo.bundleStr
: bundle 名 -
bundleInfo.originStr
: 远程路径,本地 Bundle 可为空 -
bundleInfo.versionStr
: bundle 版本(打包时的 md5)
示例#
mk.bundle.set({
bundleStr: 'subgame1',
originStr: 'https://cdn.example.com/subgame1', // 可选远程路径
versionStr: 'bundle 的 md5 版本', // 可选版本号
});
load#
加载 bundle,若已存在则直接返回
参数#
load(bundleName)
load(loadConfig)
-
bundleName
: bundle 名,其他数据从bundleMap
获得 -
loadConfig
: 加载配置 -
loadConfig.bundleStr
: bundle 名 -
loadConfig.versionStr
: bundle 版本 -
loadConfig.originStr
: bundle 远程路径 -
loadConfig.progressCallbackFunc
: 加载进度回调
loadScene#
加载/预加载场景
参数#
loadScene(sceneStr_, config_)
sceneStr_
: 场景名config_
: bundle 配置config_.bundleStr
: bundle 名config_.isPreload
: 是否是预加载,默认 falseconfig_.progressCallbackFunc
: 加载进度回调,可选config_.beforeLoadCallbackFunc
: 加载前调用的函数,可选config_.launchedCallbackFunc
: 启动后调用的函数,可选config_.unloadedCallbackFunc
: 场景卸载后回调,可选
注意:如果
config_.isPreload === true
,只做预加载并返回是否成功,不走切场景逻辑。
reload#
用于热更 bundle(替换脚本、资源)
注意:
PREVIEW 模式不支持重载
不能在正在运行该 bundle 的场景内重载(防止资源被释放报错)
参数#
reload(bundleInfo_)
-
bundleInfo_
: bundle 信息 -
bundleInfo_.bundleStr
: bundle 名 -
bundleInfo_.versionStr
: bundle 版本,可选 -
bundleInfo_.originStr
: bundle 远程路径,可选 -
bundleInfo_.ccclassRegexp
: 匹配 ccclass 名称正则表达式,可选 -
bundleInfo_.progressCallbackFunc
: 加载进度回调,可选
必要条件#
获取 ccclass 组件类型:由于内部会清理 ccclass 组件脚本缓存,所以你必须确保框架能获取到 ccclass 类型才能使用 reload
,
框架内部使用以下方式获取 ccclass 类型
-
ccclass 装饰器参数匹配
框架内置的正则匹配是
$\{bundle名}(_|/)\
,以reload
resources bundle 举例@ccclass("resources/A") class ResourcesA { ... } @ccclass("resources_A") class ResourcesA { ... }
上面两种方式都能让框架正常清理
ResourcesA
缓存,你也可以传递自己的正则表达式(bundleInfo_.ccclassRegexp
) -
导出 ccclass 类型
@ccclass("resourcesA") export class ResourcesA { ... }
使用
export
关键字导出 class 也能让框架拿到正常清理ResourcesA
缓存
注意:reload 过程会卸载旧脚本/资源,若存在跨 bundle 单例或未清理的全局引用、定时器、事件等,会导致错误。热更前请确保 bundle 数据被已正确清理
常见使用示例(代码片段)#
注册 bundle 信息并加载:
mk.bundle.set({ bundleStr: 'subgame1', originStr: 'https://cdn/x/subgame1' });
await mk.bundle.load('subgame1');
切换场景:
const ok = await mk.bundle.loadScene('Level01', {
bundleStr: 'subgame1',
progressCallbackFunc: (f, t) => console.log(f, t),
beforeLoadCallbackFunc: directorOnBeforeLoad,
launchedCallbackFunc: (err, scene) => console.log('launched', err),
});
if (!ok) {
console.error('场景切换失败');
}
热更 bundle:
await mk.bundle.reload({
bundleStr: 'subgame1',
versionStr: '0d5a2d',
originStr: 'https://cdn/x/subgame1',
ccclassRegexp: /subgame1(_|\/)/,
});
常见问题(FAQ)#
Q:什么时候触发 beforeBundleSwitch
/ afterBundleSwitch
?
仅在真实切换 bundle(bundle.name !== this._bundleStr
)时触发;在 loadScene
的切换流程中,先发 beforeBundleSwitch
,成功切换后会发 afterBundleSwitch
。监听者可通过 event.request
阻塞或异步等待。
Q:reload 会卸载哪些东西?
reload 会尝试清理脚本缓存(System / scriptCache)、注销 ccclass、释放 bundle 资源并从 assetManager
中移除旧 bundle。务必保证没有遗留的全局引用或未释放资源,否则可能抛异常或行为异常。reload
不在 PREVIEW 模式下支持。
风险与最佳实践#
- 在调用
reload
前:确保当前运行场景不是被 reload 的 bundle(不能在该 bundle 的场景内重载)。 - 使用
BundleManageBase
管理资源和网络,所有通过followRelease
注册的对象会在close()
时自动释放,降低内存泄漏风险。 - 对于复杂热更场景,先在测试环境多次验证脚本卸载与资源释放顺序,避免遗留无效 ccclass 导致
js.unregisterClass
出错。 loadScene
的事件机制是可等待的(event.request
),良好使用可避免场景切换中途被破坏的状态(比如弹窗未关闭、数据未 flush)。