我在Babel环境中的ES6中遇到了这个问题:
// A.js
class A {
}
export default new A();
// B.js
import C from './C';
class B {
}
export default new B();
// C.js
import A from './A';
import B from './B';
class C {
constructor(A, B){
this.A = A;
this.B = B; // undefined
}
}
export default new C(A, B)
我像这样导入它们:
// stores/index.js
import A from './A';
import B from './B';
import C from './C';
export {
A,
B,
C
}
从我的app入口点开始:
import * as stores from './stores';
我希望(希望)预期执行顺序为A - > B - > C但实际上它是A-> C-> B.
这是由于模块B导入C,因此在B模块之前评估C模块。这会在C的设置中产生问题,因为在这种情况下 B 将未定义。
我见过类似的question,但我不确定init函数是否是最好的解决方案,看起来有点hacky。
问:在ES6中解决这种可能适用于不同环境(Babel,Rollup)的循环依赖关系的最佳做法是什么?
答案 0 :(得分:5)
ES6中循环依赖的最佳实践是什么?
完全避免它们。如果您不能(或者不想)完全避免它们,请将所涉及的模块限制为仅使用具有循环依赖关系的函数声明,而不是通过使用初始化顶级值(常量,变量,类)导入的值(包括extends
引用)。
在不同环境中可能起作用的是什么(Babel,Rollup)?
模块解析和初始化的顺序在ES6规范中定义,因此在所有ES6环境中都应该相同 - 无论模块如何加载以及如何解析标识符。
如何解决这种循环依赖关系设置?
如果确实有循环依赖X -> Y -> Z -> … -> X -> …
,则需要建立起点。假设您希望首先加载X
,但这取决于Y
,因此您需要确保X
从不使用任何导入的值,直到圈子中的所有模块完全初始化。所以你打破了X
和Y
之间的圈子,你需要在Y
启动导入链 - 它会递归遍历依赖关系,直到它停在X
,它没有其他尚未初始化的依赖项,因此它将是第一个要评估的模块。
您必须遵循的规则是始终导入Y
,然后再导入圈子中其他模块的任何。如果您甚至一次不使用这个常见的单一入口点,那么您的纸牌屋就会崩溃。
在您的特定示例中,这意味着index.js
将需要使用
import A from './A';
import C from './C'; // entry point to circular dependencies
import B from './B';
…