[bug 记录]:循环引用的问题

  • Creator 版本: 3.6.2 - 3.7.0

  • 目标平台: 编辑器、chorme

  • 重现方式:

// a.ts

import c from "./c";

export class a {
	func() {
		c.get(null as any);
	}
}
export default a;

// b.ts

import { a } from "./a";
import c from "./c";

export class b extends a {
	func(): any {
		c.get(null!);
	}
}

export default b;

// c.ts

import b from "./b";

export class c {
	get(...args_as_: any[]): any {
		return null!;
	}

	close(args_: any): any {
		return new b();
	}
}

export default new c();
  • 首个报错:

  • 之前哪个版本是正常的:

  • 手机型号:

  • 手机浏览器:

  • 编辑器操作系统: windows

  • 重现概率: 100%

排查了很久,按照 es6 的规则难道不是只有在模块加载时使用了循环引用的导入才会报错吗?

我这函数在模块加载时都没调就在编辑器报错了

大概知道问题了,b 导入 a,a 此时导入了 c 造成循环引用返回空,b 继承导入的空值 a 就会报错

推荐个包,可以查找循环依赖,madge - npm

静态查找只能起个参考作用,如果知道更好的可以推荐下

2赞

分享一个使用动态模块解决问题的办法,正常使用动态模块都是 (await import("../xxx")).default 或者 (await module).default 这样的使用方式

而用代理可以改成下面的方式,返回的数据类型直接是模块导出类型,但是内在还是一个动态模块

ZPP%5}{5@IS

代码

import instance_base from "./instance_base";
import log from "./log";

/** 动态模块(用以解除循环引用) */
class dynamic_module extends instance_base {
	/**
	 * 获取模块默认导出
	 * @param module_ 动态模块
	 * @returns
	 */
	// @ts-ignore
	default<T extends Promise<any>>(module_: T): Awaited<T>["default"] {
		/** 模块导出表 */
		let module_export_tab: any;

		module_.then((v) => {
			module_export_tab = v;
		}) as any;

		return new Proxy(Object.create(null), {
			get: (target, key) => {
				if (module_export_tab) {
					return module_export_tab["default"][key];
				}
				log.error("模块未加载完成");
				return null;
			},
		});
	}

	/**
	 * 获取模块所有导出
	 * @param module_ 动态模块
	 * @returns
	 */
	// @ts-ignore
	all<T extends Promise<any>>(module_: T): Awaited<T> {
		/** 模块导出表 */
		let module_export_tab: any;
		/** 模块导出代理表 */
		const module_export_proxy_tab = {};

		module_.then((v) => {
			module_export_tab = v;
		}) as any;

		return new Proxy(Object.create(null), {
			get: (target, key) => {
				if (module_export_proxy_tab[key] === undefined) {
					module_export_proxy_tab[key] = new Proxy(Object.create(null), {
						get: (target2, key2) => {
							if (module_export_tab) {
								return module_export_tab[key][key2];
							}
							log.error("模块未加载完成");
							return null;
						},
					});
				}
				return module_export_proxy_tab[key];
			},
		});
	}
}

export default dynamic_module.instance();

我现在已经极少遇到循环引用了,我发现只要多写几个类就能避免大部分循环引用的情景 :sweat_smile:

很多时候循环引用的来源是把继承关系、常量和工厂写在了一个类里了,就特别容易循环

同为新手也十分担心循环引用问题。感谢分享。

我们现在导入脚本时,会提示循环引用的警告的。为什么还需要借助第三方 madge - npm 呢?

因为只警告了哪里导入失败,整个循环链是什么都没办法确定

循环引用是一个js里面很麻烦的问题,往往发现的比较晚,然后去改的时候发现要改动很多地方或者改不了,容易出现新的bug,最好在写完一大堆代码后可以手动执行一下检查,开发时发现问题然后解决