2010-03-07 161 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一個模塊加載器。