【问题标题】:Circular dependency issue with Typescript, CommonJS & BrowserifyTypescript、CommonJS 和 Browserify 的循环依赖问题
【发布时间】:2015-01-25 02:09:06
【问题描述】:

我正在将一个相当大的打字稿项目从内部模块转移到外部模块。我这样做是因为我想创建一个核心包,如果需要,它可以加载其他包。我要记住的第二个要求是,我希望能够使用 nodeJS 在服务器上运行捆绑包(如果需要,可以进行一些修改)。

我第一次尝试使用 AMD 和 require.js 来构建核心包,但我遇到了循环依赖的问题。在阅读完这与 require.js 很常见之后,commonJS 更常被建议用于大型项目,我尝试过。但是现在它与 browserify 一起设置,当我运行编译的包时,我在完全相同的位置出现了 exact 相同的问题。

我有 10 个基类,它们相互依赖并形成多个循环依赖。我看不出有任何方法可以删除所有这些。

一个简化的设置来解释为什么我认为我不能删除循环依赖:

Triples are made of 3 Resources (subject,predicate,object)
Resource has TripleCollections to keep track of which triples its used in
Resource has multiple functions which rely on properties of Triple
Triple has some functions which handle TripleCollections
TripleCollection has multiple functions which uses functions of Triple
TripleCollection.getSubjects() returns a ResourceCollection
ResourceCollection.getTriples() returns a TripleCollection
Resource keeps track of the objects of its triples in ResourceCollections
ResourceCollection uses multiple functions of Resource

我在 SO 上阅读了多个相关问题(this one 最有帮助),据我所知,我只有几个选择:

1) 将所有循环依赖的基类放在一个文件中。

这意味着它将成为一个地狱般的文件。而且导入总是需要别名:

import core = require('core');
import BaseA = core.BaseA;

2) 使用内部模块

当我使用内部打字稿模块和参考文件时,核心包运行良好(具有循环依赖项)。但是,如果我想创建单独的包并在运行时加载它们,我将不得不对所有带有 require.js 的模块使用垫片。


虽然我不太喜欢所有的别名,但我想我现在会尝试选项 1,因为如果它有效,我可以继续使用 CommonJS 和 browserify,稍后我还可以更轻松地在节点中运行服务器上的所有内容.如果 1 不起作用,我将不得不求助于选项 2。

Q1:有没有我没有提到的可能的解决方案?

Q2:对于具有 1 个核心捆绑包的 typescript 项目的最佳设置是什么,该捆绑包可按需加载其他捆绑包(基于核心构建)。这似乎无法绕过循环依赖。并且最好可以在客户端和服务器上运行。

或者我只是在寻求不可能的事情? :)

【问题讨论】:

    标签: typescript circular-dependency browserify commonjs


    【解决方案1】:

    简单地说(也许很简单,但我不这么认为),如果你有ModuleAModuleB 并且两者都相互依赖,那么它们就不是模块。它们位于单独的文件中,但它们的行为不像 模块

    在你的情况下,这些类是高度相互依赖的,你不能使用其中任何一个类而不需要它们,所以除非你可以重构程序以尝试使依赖关系单向,而不是双向(例如),我会将这组类视为一个模块。

    如果您确实将所有这些放在一个模块中,您仍然可以通过移动一些依赖项将其分解为模块,例如(所有这些问题在很大程度上都是修辞性的,因为它们是一种问题你需要问自己),TripleResource 彼此分享什么?可以将其转移到他们都可以依赖的类中吗?为什么Triple 需要知道TripleCollection(可能是因为这代表了一些分层数据)?您可能只能移动一些东西,但从当前设计中删除的任何依赖项都会降低复杂性。例如,如果TripleResource 之间的双向关系可以被删除。

    您现在可能无法大幅更改此设计,在这种情况下,您可以将其全部放在一个模块中,这将在很大程度上解决模块加载问题,并且还将可能更改的代码保持在一起出于同样的原因。

    这一切的总结是:

    如果你有一个双向的模块依赖,让它成为一个单向的模块依赖。通过移动代码以使依赖以一种方式实现这一点,或者将其全部移动到一个更大的模块中,该模块更真实地表示模块之间的耦合。

    所以我对你的选择的看法与你的问题略有不同......

    1) 尝试重构代码以减少耦合,看看是否可以保留更小的模块

    失败了……

    2) 将所有循环依赖的基类放在一个文件中。

    我不会将内部模块选项放在列表中 - 我认为外部模块是管理大型程序的更好方法。

    【讨论】:

    • 谢谢史蒂夫,你真棒。我将花更多时间尝试删除两种方式的依赖关系,并将内部模块从列表中删除。关于什么是模块 的注释也很有帮助。因为现在——如果我最终把东西放在一个文件中——它会感觉不像是一种解决方法,而更像是模块工作方式的自然结果。所有这一切确实帮助我更加确定地前进
    • @Flion 用于重构和类到接口工作,您可能会发现Dependency Structure Matrix 很有用,尤其是某些工具提供的一些自动分区算法,例如lattix.com/videos
    • 我认为让我感到困惑的是试图完全按照我的数据库结构对我的 javascript 类进行建模。当然它们应该彼此相似,但是在数据库设计中并没有禁止循环依赖,这让我很困惑。一旦我意识到它们有不同的用途,就更容易掌握每个应该如何建模。
    • 这是一个真正帮助我处理 TypeScript 代码中的循环依赖关系的插件。 npmjs.com/package/circular-dependency-plugin 如果没有这样的插件,您最终会发现这些问题是运行时的,这是一场灾难。检测循环依赖应该自动内置到每个模块加载器中,因为众所周知,事情会在运行时失败,模块在运行时莫名其妙地为空。很遗憾,现在是 2017 年,开发人员仍然不得不为此争论不休。这导致开发人员生产力损失数十亿美元。
    猜你喜欢
    • 2014-08-28
    • 1970-01-01
    • 2019-01-10
    • 2017-10-27
    • 2016-02-27
    • 2019-12-09
    • 1970-01-01
    相关资源
    最近更新 更多