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 模式不支持 reload
  • 需要在打包时开启 MD5 缓存
  • 在当前 Bundle 的场景内 reload 当前 Bundle 的操作将延迟到切换到其他 Bundle 的场景后执行

参数#

reload(bundleInfo_)

  • bundleInfo_: bundle 信息

  • bundleInfo_.bundleStr: bundle 名

  • bundleInfo_.versionStr: bundle 版本,可选

  • bundleInfo_.originStr: bundle 远程路径,可选

  • bundleInfo_.ccclassRegexp: 匹配 ccclass 名称正则表达式,可选

  • bundleInfo_.progressCallbackFunc: 加载进度回调,可选

必要条件#

获取 ccclass 组件类型: 由于需要清理引擎内 ccclass 组件脚本缓存,但框架只能获取到所有 ccclass 组件(不能识别所属 Bundle), 或者 Bundle 中脚本导出的组件类型,所以必须保证框架能获取所有属于这个 Bundle 的组件类型,框架提供了两种方式

  1. ccclass 装饰器参数匹配

    框架内置的正则匹配是 $\{bundle名}(_|/)\,以 reload resources bundle 举例

    @ccclass("resources/A")
    class ResourcesA extends XXX { ... }
    
    @ccclass("resources_A")
    class ResourcesA extends XXX { ... }
    

    上面两种方式都能让框架识别这个组件类型属于名为 resources 的 Bundle

    自定义正则表达式: bundleInfo_.ccclassRegexp

  2. 导出 ccclass 类型

    @ccclass("A")
    export class A { ... }
    

    使用 export 关键字导出组件的 class 也能让框架识别并清理 A 组件的缓存

    此方式不适用于多重嵌套,例如把 A 放在 namespace 或者 module 块中导出,框架不会获取到这个组件

小提示:上面两种方式可以混合使用

注意事项#

reload 过程会卸载旧脚本/资源,若存在跨 bundle 单例或未清理的全局引用、定时器、事件等,会导致错误。 热更前请确保 bundle 数据被已正确清理

getCache#

用于获取本地缓存中的远程 bundle 信息(上次加载的 Bundle Url 及版本号)

参数#

getCache(bundleStr_)

  • bundleStr_: 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 模式下支持。


风险与最佳实践#

  • 使用 BundleManageBase 管理资源和网络,所有通过 followRelease 注册的对象会在 close() 时自动释放,降低内存泄漏风险。
  • 对于复杂热更场景,先在测试环境验证,避免遗留无效 ccclas
  • loadScene 的事件机制是可等待的(event.request),良好使用可避免场景切换中途被破坏的状态(比如弹窗未关闭、数据未 flush)。