2010-03-07 164 views
4

在复杂的客户端项目中,Javascript文件的数量可能非常大。但是,出于性能方面的原因,最好将这些文件连接起来,并压缩生成的文件以通过网络发送。我在连接这些时遇到了问题,因为在某些情况下需要依赖项后才会包含这些依赖项。Javascript对象依赖关系

例如,有2个文件:

/modules/Module.js <requires Core.js> 
/modules/core/Core.js 

的目录遍历递归和Module.jsCore.js,这会导致错误之前包括在内。这只是一个简单的例子,依赖可能跨越目录,并且可能还有其他复杂的情况。虽然没有循环依赖关系。我遵循的Javascript结构类似于Java包,其中每个文件都定义了一个对象(我使用的是MooTools,但这不相关)。每个javascript文件和依赖的结构始终保持一致:

Module.js

var Module = new Class({ 
    Implements: Core, 

    ... 
}); 

Core.js

var Core = new Class({ 
    ... 
}); 

什么做法,你通常遵循处理项目中的依赖关系,其中Javascript文件数量巨大,并且存在文件间依赖关系?

回答

1

有一个非常粗略的依赖查找器,我写了基于我正在做串联。事实证明,其使用的MooTools毕竟不是那么无关紧要。该解决方案效果很好,因为它不需要单独维护依赖信息,因为它在JavaScript文件本身中可用,这意味着我可以超级懒惰。

由于类和文件命名一致,类Something将始终具有文件名Something.js。为了找到外部依赖,所以我在寻找三件事情:

  1. 做它Implement任何其他 类
  2. 做它Extend任何其他 类
  3. 它使用new关键字实例其他类

在每个javascript文件中搜索上述三种模式会给出它的相关类。找到依赖类后,搜索所有驻留在任何文件夹中的Javascript文件,并与该类名称进行匹配,找出该类定义在哪里。一旦找到依赖关系,我就构建一个dependency graph并使用topological sort算法来生成包含文件的顺序。

0

我说只是将这些文件以有序的方式复制并粘贴到一个文件中。每个文件都有一个开始和结束的注释来区分每个特定的代码。

每次更新其中一个文件时,都需要更新此文件。所以,这个文件只需要包含完成库,这个文件不会在近期发生变化。

+0

大声笑,我有超过100个js文件,这只是早期阶段..可以创建一个新的全职工作只是为了这个目的:P – Anurag 2010-03-07 00:52:40

+0

一百个文件?什么??? – 2010-03-07 00:54:45

+0

通过创建另一个文件'dependencies.list',按照应该包含的顺序列出所有js文件(包含完整路径),轻松修改您的解决方案。现在构建脚本可以创建一个'temp'文件,遍历'dependencies.list'中列出的每个文件的内容,并保持附加到'temp'。这样只有一个文件需要改变,并且维护顺序。只有两个人在这方面工作,这也将变得复杂。 – Anurag 2010-03-07 01:00:01

0

你的目录结构倒置......

核心的依赖应该是根和模块的子目录。

scripts/core.js 
scripts/modules/module1.js 

您的问题已解决。

任何进一步的依赖性问题将表明有缺陷的“类”/依赖性设计。

+0

这只是一个例子,也许我选择的名字不是很合适。但问题基本上是关于依赖关系。与核心组件无关。 – Anurag 2010-03-07 01:11:35

+0

@Auurag - 答案是一样的无论。如果要从递归目录扫描加载脚本,请将依赖关系放在比依赖项更靠近根的地方。简单。 – 2010-03-07 01:27:18

+0

我们的结构就像包(Java或其他)。如果包装结构设计得很好,对于更接近根部的东西没有限制。而且,在同一个软件包(我们确实有这个软件包)中可能存在依赖关系,并且将文件重命名为首先出现在某个列表中,无论如何我们正在执行此操作(直到现在)。 – Anurag 2010-03-08 06:00:13

0

与Mendy类似,但我在服务器端创建了组合文件。创建的文件也将被缩小,并且将具有唯一的名称以在更新后省略缓存问题。

当然,这种做法只有在整个应用程序或框架中才有意义。

+0

您是否在服务器端脚本的某个位置定义了依赖关系,以便在连接时进行考虑? – Anurag 2010-03-07 01:21:18

+0

服务器端脚本定义了所需的文件列表。并没有一个js文件,但像'array('media.js'=> array('a.js','b.js',...),'mmgt.js'=> .. )'。因此,一个服务器端脚本可能需要media.js,另一个可能需要media.js和mmgt.js等。 – Frunsi 2010-03-07 02:19:56

0

我认为,如果可能的话,最好的办法就是重新设计,以免有大量javascript文件与文件间依赖关系。 JavaScript只是不打算去那里。

+0

它适用于RIA,其中所有视图和所有逻辑都是使用JavaScript从头开始构建的。只有在需要数据时才会与远程服务进行通信。实际上大部分文件都非常短,可能只有6-10行,但它在管理复杂性方面有很大帮助。 – Anurag 2010-03-07 01:07:30

1

这可能很粗鲁,但我所做的是将单独的脚本片段保存在单独的文件中。我的项目是这样的,我愿意为每个页面提供所有的Javascript(因为毕竟它会被缓存,而且我不会注意到解析步骤中的性能问题)。因此,在构建时,我的Ant脚本通过一个小的自定义Ant任务运行Freemarker。该任务源于源代码树并将所有单独的Javascript源文件收集到一组地图中。有几种不同的源代码(jQuery扩展,一些页面加载操作,一般实用程序等),因此任务将这些不同类型组合在一起(从脚本源代码目录结构中获取它的提示。

一旦它建立了地图,它会将它们提供给Freemarker。这里有一个全局模板,通过Freemarker所有的脚本片段被打包到一个文件中,然后通过YUI压缩器,并且宾果每页都抓取那一个脚本,一旦它被缓存了,我的整个站点就没有更多的脚本了。

依赖关系,你问?那么,Ant任务按名称排序我的源文件,因为它建立这些映射,所以我需要确保定义使用排序我只是在文件前加上与数字代码。 (在某些时候,我打算把它放开,以便源文件可以保留它们的订购信息,或者甚至可以明确声明依赖关系,在评论块的源代码内部。我不会太积极,因为它虽然有点丑,它真的不会打扰任何人那么多。)

+0

我喜欢使用目录名和文件扩展名将事物分组在一起的想法。有趣的是,你说的依赖关系,因为我只是这样做(前缀下划线)直到今天,当事情停止工作后,当只有少量js文件添加到项目中时,它真的开始烦我了。 – Anurag 2010-03-07 01:18:36

0

这可能是太明显了,但你看MooTools的核心Depender:http://mootools.net/docs/more/Core/Depender

+0

核心Depender看起来非常好,但我目前的项目是在Rails中,我不认为有一个用于Dependent.Server的rails组件。另外,只要我坚持某些惯例,我的方法应该不需要手动指定依赖关系。 – Anurag 2010-03-08 19:57:43

+0

1.3即将推出,为服务器端js提供了很多好东西,包括CommonJS支持,rails东西等等。等待:) – 2010-03-09 09:02:28

+0

不能等待1.3! – Anurag 2010-03-11 03:25:36

2

使用目录很聪明,但是,我认为当你有多个依赖,你可能会遇到问题。我发现我必须创建自己的解决方案来处理这个问题。所以,我创建了一个值得检查的依赖管理工具。 (Pyramid Dependency Manager documentation

它一些重要的事情其他JavaScript依赖管理者不要做,主要是

  1. 处理其他文件(包括意见插入HTML ...是的,你可以在开发过程分开你的看法)
  2. 当您准备发布时(无需安装外部工具),您可以将JavaScript中的文件合并到您的JavaScript中
  3. 对所有html页面都有一个通用的包含。您只需要在添加,删除,重命名等依赖项时更新一个文件。

一些示例代码,显示它在开发过程中如何工作。

文件:dependencyLoader.js

//Set up file dependencies 
Pyramid.newDependency({ 
    name: 'standard', 
    files: [ 
    'standardResources/jquery.1.6.1.min.js' 
    ] 
}); 

Pyramid.newDependency({ 
name:'lookAndFeel', 
files: [ 
    'styles.css', 
    'customStyles.css' 
    ] 
}); 

Pyramid.newDependency({ 
name:'main', 
files: [ 
    'createNamespace.js', 
    'views/buttonView.view', //contains just html code for a jquery.tmpl template 
    'models/person.js', 
    'init.js' 
    ], 
    dependencies: ['standard','lookAndFeel'] 
}); 

HTML文件打破分析时或加载时的依赖

<head> 
    <script src="standardResources/pyramid-1.0.1.js"></script> 
    <script src="dependencyLoader.js"></script> 
    <script type="text/javascript"> 
     Pyramid.load('main'); 
    </script> 
</head> 
0

一种方法是使用自定义对象(一在Self-Defining Functions上的变化)。

比方说,你有这样的事情:

var obj = new Obj(); 

这条线是someFile.js和obj是在Obj.js.定义为了成功解析,您必须在someFile.js之前加载或连接Obj.js。

但如果你定义OBJ这样的:

​​

然后在分析或加载时间也不要紧,你加载什么顺序,只要obj是在运行时可见的两个文件。你必须调用obj.init()才能让你的对象进入你想要的状态,但这对于打破依赖关系来说是一个很小的代价。

只是为了更清楚如何工作这里是一些代码,您可以剪切并粘贴到浏览器控制台:

var Obj = function() { 
    this.func1 = function () { 
     console.log("func1 in constructor function"); 
    }; 
    this.init = function() { 
     console.log("init in constructor function"); 
    } 
}; 

var obj = { 
    init: function() { 
     console.log("init in original object"); 
     obj = new Obj(); 
     obj.init(); 
    } 
}; 

obj.init(); 
obj.func1(); 

而且你也可以尝试像RequireJS一个模块加载器。