[muzzik 分享]:热更 bundle 脚本(不修改引擎)

测试环境

版本:3.7.0
平台:[web-desktop-debug, android-debug-md5, android-release-md5]

注意事项

  • 重载 bundle 时不能在 bundle 内的场景

  • 重载前需要进行数据清理操作(例如定时器、节点池、监听的全局事件)

  • bundle 内的 ccclass 清理并没有很好的方法,所以是按照类名匹配,需要规范 @ccclass 装饰器的参数前缀

使用代码

/** bundle 脚本表 */
const bundle_script_tab: Record<string, any> = {};
/** js 系统 */
const system_js = self["System"];
/** 脚本缓存表 */
const script_cache_tab: Record<string, any> = system_js[Reflect.ownKeys(system_js).find((v) => typeof v === "symbol")];

// 初始化 bundle 脚本表
Object.keys(script_cache_tab).forEach((v_s) => {
	const current = script_cache_tab[v_s];
	const parent = script_cache_tab[v_s].p;
	const child = parent.d;

	if (!parent || !child || current.id !== parent.id) {
		return;
	}

	const name_s = parent.id.slice((parent.id as string).lastIndexOf("/") + 1);

	bundle_script_tab[name_s] = parent;
});

// 清理脚本缓存
{
	const bundle_root = bundle_script_tab[bundle_s];

	if (bundle_root) {
		bundle_root.d.forEach((v: { id: string }) => {
			delete script_cache_tab[v.id];
			delete system_js["registerRegistry"][v.id];
		});
		delete script_cache_tab[bundle_root.id];
		delete system_js["registerRegistry"][bundle_root.id];
	}
}

// 清理 ccclass
{
	const reg = new RegExp(`${bundle_s}(_|/)`);

	Object.keys(cc.js._nameToClass)
		.filter((v_s) => v_s.match(reg) !== null)
		.forEach((v_s) => {
			cc.js.unregisterClass(cc.js.getClassByName(v_s));
		});
}

// 清理 bundle 资源
{
	const bundle = cc.assetManager.getBundle(bundle_s);

	if (bundle) {
		bundle.releaseAll();
		cc.assetManager.removeBundle(bundle);
	}
}

// ... 加载 bundle

参数

  • bundle_s:bundle 名

实现参考

10赞

类名重复坏大事,不过已经很好了

马克~~~~

:+1: 666

大佬,请问有demo代码吗?来晚了各位讲得我云里雾里的

大佬 非ccclass的类是不是就不自动更新了?

清理脚本缓存那步就是清理所有脚本(包含ccclass的脚本),只是ccclass除了脚本缓存还有额外的引擎缓存,然后重新加载bundle的时候才会注册新的脚本内容

1赞

就是预制体绑定的脚本导致的缓存咯? 谢谢大佬

bundle_s是什么东西?我应该在哪里使用这个脚本?

框架内有热更示例

战术Mark 战术Mark

战术Mark 战术Mark

战术Mark 战术Mark

mark-

历害了。。。。