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: 是否是预加载,默认 false
  • config_.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)。