ES6模块和循环依赖

时间:2017-10-05 15:53:21

标签: javascript module ecmascript-6 circular-dependency 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)

我像这样导入它们:

// 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)的循环依赖关系的最佳做法是什么?

1 个答案:

答案 0 :(得分: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';
…