使用 SDP 杜绝循环依赖

循环依赖是“沉默的杀手”。它们不会导致构建错误,也不会在导入时引发运行时异常。相反,它们会产生微妙的 undefined 值,并在几周后的生产环境中才显现出来。

当模块 A 从 B 导入,而 B 又从 A 导入时,就会发生循环。

JavaScript 会静默地解析这些导入。如果模块 B 请求模块 A 时,模块 A 仍在加载中,JavaScript 会返回一个空对象。这会导致你的代码在稍后运行失败,而堆栈跟踪(stack trace)指向的位置却是错误的。

你可以使用 dependency-cruiser 来解决这个问题。该工具可以扫描你的文件并映射你的依赖图。它支持 TypeScript 和 monorepos。

如何进行设置:

  • 将其添加到开发依赖中:yarn add -D dependency-cruiser
  • 在你的 package.json 中添加一个脚本:"depcruise": "depcruise packages --config .dependency-cruiser.js"
  • 创建一个 .dependency-cruiser.js 文件来捕获循环。

但仅仅发现循环只是成功了一半。你必须阻止它们的形成。

使用稳定依赖原则 (Stable Dependencies Principle, SDP)。该规则指出,你必须朝着“稳定性”的方向进行依赖。

稳定性是一种结构属性。当许多其他模块都依赖于某个模块时,该模块就是稳定的。如果你更改了一个稳定的模块,你会影响到很多人。这种成本使其变得稳定。

使用不稳定性 (Instability) 公式: I = Fan-Out / (Fan-In + Fan-Out)

I = 0 表示模块是最大程度稳定的。一切都依赖于它,而它不依赖于任何东西。 • I = 1 表示模块是最大程度不稳定的。它依赖于很多东西,而没有任何东西依赖于它。

规则:一个模块的不稳定性分数必须高于它所导入的每一个模块的分数。你的依赖箭头必须指向不稳定性更低的方向。

你的底层工具函数 (I ≈ 0) 绝不应该从高层页面 (I ≈ 1) 导入。

你可以使用 dependency-cruiser 将这一原则转化为自动化规则。与其仅仅检查循环,不如检查依赖方向:

  • 防止 utilsfeatures 导入。
  • 防止 featurespages 导入。

这从根本上阻止了产生循环的结构性条件。你不再是消极地应对 Bug,而是开始主动预防它们。

Source: https://dev.to/wojciech_kot_b82f5d7cbfc6/stop-circular-dependencies-before-they-stop-you-dependency-cruiser-the-stable-dependencies-34ho