ES6模块和循环依赖

Leo*_*rdo 2 javascript module circular-dependency ecmascript-6 es6-modules

我在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)
Run Code Online (Sandbox Code Playgroud)

我像这样导入它们:

// stores/index.js
import A from './A';
import B from './B';
import C from './C';

export {
    A,
    B,
    C
}
Run Code Online (Sandbox Code Playgroud)

从我的应用程序入口点我做:

import * as stores from './stores'; 
Run Code Online (Sandbox Code Playgroud)

我希望(希望)预期执行顺序为A - > B - > C但实际上它是A-> C-> B.
这是由于模块B导入C,所以C模块在之前被评估B模块.这在C的设置中产生了问题,因为在那种情况下B将是未定义的.

我已经看到了类似的问题,但我不确定init函数是否是最好的解决方案,看起来有点hacky.

:在ES6中解决这种可能适用于不同环境(Babel,Rollup)的循环依赖的最佳实践是什么?

Ber*_*rgi 5

ES6中循环依赖的最佳实践是什么?

完全避免它们.如果您不能(或者不想)完全避免它们,请将所涉及的模块限制为使用具有循环依赖关系的函数声明,而不是使用导入的值(包括extends引用)初始化顶级值(常量,变量,类) .

什么可能在不同的环境中工作(Babel,Rollup)?

模块解析和初始化的顺序在ES6规范中定义,因此在所有ES6环境中都应该相同 - 无论模块如何加载以及如何解析标识符.

如何解决这种循环依赖设置?

如果确实存在循环依赖关系X -> Y -> Z -> … -> X -> …,则需要建立起点.假设您希望X首先加载,尽管它依赖于此Y,因此您需要确保X从不使用任何导入的值,直到圆圈中的所有模块都完全初始化为止.所以,你打破的圆圈XY,你将需要在开始导入链Y-它会递归遍历的依赖,直到它停止在X,它具有尚未得到初始化没有进一步的依赖关系,所以这将是第一个模块被评估.

您必须遵循的规则是在导入圆圈中的任何其他模块之前始终导入.如果您甚至一次不使用这个常见的单一入口点到圆圈,您的纸牌将崩溃.Y

在您的特定示例中,这意味着index.js需要使用

import A from './A';
import C from './C'; // entry point to circular dependencies
import B from './B';
…
Run Code Online (Sandbox Code Playgroud)