【问题标题】:ES6 Modules and Circular DependencyES6 模块和循环依赖
【发布时间】:2018-03-17 07:53:09
【问题描述】:

我在 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
}

从我的应用入口点开始:

import * as stores from './stores'; 

我希望(希望)执行顺序是 A -> B ->C,但实际上它是 A-> C-> B。
这是由于模块 B 导入了 C,因此 C 模块在 B 模块之前被评估。这会在 C 的设置中产生问题,因为在这种情况下 B 将是 undefined

我见过类似的question,但我不确定 init 函数是不是这里的最佳解决方案,它似乎有点 hacky。

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

【问题讨论】:

  • 预计执行顺序是 A -> B ->C - 哪个入口点?为什么 C 甚至不使用都导入到 B 中?
  • 它被使用,这是一个缺少实现细节的例子。我已经更新了添加入口点的问题
  • 省略的细节改变了这里的一切。不会有循环依赖,因为跳过了未使用的导入。考虑提供 stackoverflow.com/help/mcve 。仅对单例使用类也是反模式。
  • Anew C() 中的 B 未定义,因为您没有向构造函数传递任何参数,而不是因为导入不起作用。
  • 从不导出类的实例。如果你想要单例模块,不要使用class 语法。

标签: javascript module ecmascript-6 circular-dependency es6-modules


【解决方案1】:

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';
…

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-14
    • 2014-04-28
    • 1970-01-01
    • 2017-10-27
    • 2021-09-18
    • 2014-04-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多