图表 - 如何建模依赖资源?

时间:2013-04-06 17:30:54

标签: javascript

没有库,我正在尝试学习数据结构。

我有这些依赖

jquery.js->jqueryui.js

(underscores.js, jquery.js) -> backbone.js

基本上,jqueryui依赖于jquery。 Bacbkone依赖于下划线和jquery。 Jquery和下划线没有关联。

我想创建一个依赖树,让你“揭开”这些关系。

有人告诉我这是如何在posted question完成的。特别是这个评论。

  

只要你没有循环依赖,你就可以随时建立一个   依赖森林,仅由有向树和/或鞋底组成   节点。在树上你可以简单地使用DFS。然后你开始添加   将所有根或单个节点放入队列并添加其他资源   加载依赖项时排队。 (请注意,如果是资源   有几个依赖项,您不能将依赖项建模为   森林,但它保持非循环,你可以使用类似的方法)。 -   泽塔

...所以我确实拥有多个依赖项的资源,所以我不能使用依赖林。

......进一步的讨论提出了有向无环图。

  

有向无环图。从起点开始的每条路径都可以   并行完成,但是如果一个节点有多个事件边缘   你必须等待加载所有依赖项。顺便说一句,我   将示例3表示为P:[U-underscore,U-jquery]   S:[U-underscore,U-backbone-js] S:[U-jquery,U-backbone.js],显示   原始的依赖,但它们是等价的

我可以使用依赖树吗?如果不是建议使用什么数据结构来建模复杂的依赖关系......最后我该如何实现它呢?

3 个答案:

答案 0 :(得分:1)

我相信我已经解决了这个问题,尽管很久以前。让依赖关系像这样描述:

  • 模块A.
    • 模块X
    • 模块Y
  • 模块B.
    • 模块X
    • 模块A
  • 模块C.
    • 模块A
    • 模块B

这意味着模块A依赖于模块X和模块Y - 依此类推。

迭代这个林中的叶子,并且对于没有依赖关系的每个叶子(在底部查看),将叶子放入加载队列并将其从森林中移除,因此首先通过产生:

  • 模块A
  • 模块B.
    • 模块A
  • 模块C.
    • 模块A
    • 模块B

队列:模块X,模块Y。

第二遍:

  • 模块B
  • 模块C.
    • 模块B

队列:模块X,模块Y,模块A。

......等等。在同一个传递中找到的模块可以并行加载,因此表示它的方法可以是:

[[Module X, Module Y], [Module A], [Module B], [Module C]]

这意味着应首先加载模块X和模块Y,并且可以并行加载它们。其余的必须按顺序加载。

我对上述方法的最大担心是它具有复杂度O(n ^ 2)。应该可以改进。使用查找映射可以轻松完成检测周期。

答案 1 :(得分:1)

考虑到你在没有库的情况下询问模块化代码但是使用库作为模块(jQuery,jQuery-ui等),似乎这个问题实际上是两个问题。

  1. 如何理解您拥有的许多外部库(无论是否依赖于使用脚本标记的线性加载)
  2. 如何实现模块化依赖
  3. 要回答第一个,它有点复杂。大多数模块化依赖设计需要在模块周围进行一些包装以跟踪它们。大多数JS库都没有使用这样的系统,假设它们将通过脚本标签加载(线性加载)。某些系统(如require.js)将提供兼容的修改版本,而其他系统则尝试以预定义的顺序将脚本标记注入页面。更流行的解决方案是使用构建工具将您拥有的不同库文件连接到一个大文件中,该文件将以正确的顺序对它们进行排序。

    大多数库都很好,并且会在里面包含它们的代码,以防止破坏其他也加载的脚本。甚至jQuery提供了noConflict()方法来阻止$语法破坏其他库可能期望的内容(例如Zepto)。

    处理与外部库的依赖关系的答案取决于修改库以符合您所拥有的模块化系统,或者具有外部系统(例如构建环境)(将缺少更好的术语)编译成正确的顺序。

    这是坏消息。好消息是你控制的代码实际上可以使用一个非常有效的依赖树。 require.js是这样做的一个主要例子。虽然诸如CommonJSAMD之类的系统可以非常强大,但您可以自己实现一个简单的依赖API。

    Rye.js获取example如何为您的代码实现自己的模块化系统:

    (function(global) {
    
      var module_list = {};
    
      global.require = function (module) {
        return module_list[module];
      };
    
      global.define = function (module, fn) {
        modules[module] = fn();
      };
    
    })(this);
    

    然后你可以定义自己的模块:

    define("hello_world", function() {
    
      function helloWorld() {
        alert("Hello World!");
      }
    
      return {
        sayHello: helloWorld
      };
    
    });
    

    然后在另一个依赖于那个的模块中:

    define("greetings", function() {
    
      var hello = require("hello_world");
    
      function sayAll() {
        hello.sayHello();
        alert("Good-Bye!");
      }
    
      return {
        sayAll: sayAll
      };
    
    });
    

答案 2 :(得分:1)

我在之前的回答中向您展示的数据结构

deps = {
    "U-jqueryui": ["U-jquery"],
    "group1": ["U-underscore", "U-jquery"],
    "U-backbone.js": ["group1"]
}

表示DAG:

U-jquerui   U-backbone.js
     |          |
     |          v
     |       group1
     |      /     |
     v    L       v
 U-jquery    U-underscore.js

由此,您可以提取依赖关系树,例如

     root
    |    |
    v    v

U-jquery U-underscore.js

代表group1。然后将所有可能树的集合称为forest

  

所以我确实拥有多个依赖项的资源,所以我不能使用依赖项林。

不,你没有。拥有多个依赖项的资源只意味着您需要树而不是队列。如果你的DAG图中有菱形,例如你有一个依赖于{{1的模块U-myapp,那么它就变为no(multi- / poly-)树了。 }和U-jquerui - 它依赖于U-backbone.js“两次”。

但是,我认为我们不需要对整个图表使用任何算法。

  

如果不是建议使用什么数据结构来建模复杂的依赖关系

我已经向您展示的那个 - 只是建模直接依赖关系。它允许您表示DAG(包括多森林),即使您还不需要它。

  

最后我该如何实现呢?

我会带着回忆的承诺(抱歉,我喜欢它们)。每个承诺都代表了一个模块已被加载和执行的事实;我们按模块名称记忆(缓存)它们,所以我们不会多次启动它们。然后代码很简单:

U-jquery