2010-09-08 56 views
41

如何在整個網站上組織大型JS/jQuery代碼庫?關於如何組織代碼,有很多很好的資源,但沒有關於如何將它們放在一起並將每個代碼組合到一起:側面廣泛的代碼組織,使用相同代碼的多個頁面,使用鬆散耦合保持DRY,等等。如何在整個網站上組織大型JS/jQuery代碼庫?

以下是我如何處理它。我從來沒有像我這樣習慣於組織代碼,因爲我覺得它很瑣碎,可能導致可維護性/擴展性問題,但我並不知道更好。

我意識到每個人都有自己的一套要求,沒有交鑰匙解決方案,但我很想聽聽我做錯的一些意見,爲什麼我做錯了,如何編寫更易維護的代碼。

我想我真正想要知道的:

  1. 你是如何處理的邏輯,你 需要在多個地方使用,在 多頁?

  2. 如何組織頁面特定的 代碼?將每個頁面命名爲 是一個好主意嗎?

  3. 那你從一開始就做 確保您不經常 重新寫你的組織 模式爲您的應用程序變得更大 和更大?我可能在我的第4個 迭代寫這個東西。

每個頁面接收主application.js文件。每個附加頁面都有自己的 application.pagename.js文件。我使用服務器端邏輯來包含這些文件(首先查看 是否存在頁面 - 有些頁面不需要JS),然後按順序初始化它們。

所以我的主頁看起來像:

<script src="js/application.js"></script> 
<script src="js/application.index.js"></script> 
<script> 
    MyApp.init(); 
    MyApp.index.init(); 
</script> 

我的網址約定/頁/子頁/ ID /。我有大約10頁和一大堆子頁面,每個子頁面都需要自己的邏輯。看到這篇文章的最後一個例子。

我的代碼大部分已經模塊化爲jQuery UI小部件或jQuery插件,所以我會說這些文件中的代碼的75%是require()'小部件並啓動它。

我使用requireJS來根據需要拉入小部件。

// application.js 
var MyApp = { 
    init: function(){ 
     var self = this; 

     // these widgets are available on every single page 
     // notice the call to jquery.deparam.js - i'll use this later to init subpage logic. 
     require(['js/widget1.js', 'js/widget2.js', 'js/widget3.js', 'js/jquery.deparam.js'], function(){ 

      // deparam the query string. I'll use this later. 
      self.querystring = $.deparam.querystring(); 

      // init widgets once the document is ready 
      $(function(){ 
       $("#widget1").widget1(); 
       $("#widget2").widget2(); 

       // make these bindings available immediately as well. 
       self.rebindable(); 
      }); 
     }); 
    }, 

    // I use jQuery UI Dialog extensively as a window manager, and each dialog is loaded 
    // via an AJAX request. I'll call this method after each AJAX request to 
    // rebind some key widgets. 
    rebindable: function(){ 
     $("#widget3").widget3(); 
    } 
}; 

// application.index.js 
// home page specific stuff. this file is only included on the home page. 
MyApp.index = { 

    // my convention is that init is automatically called after the script 
    // is included in a page, outside of a doc.ready statement. 
    init: function(){ 
     var self = this; 

     require(['js/widget4.js'], function(){ 
      $(function(){ 
       self.widget4($("#foo")); 
      }); 
     }); 
    }, 

    // passing elements to each method allows me to call this init code 
    // outside of the index page. I can require() this file, and only init 
    // widget4, and even use a different element. 
    widget4: function(element){ 
     var config = { 
      something: "custom to the home page" 
     }; 

     element.widget4(config); 
    } 
}; 


// application.foo.js 
// page "foo" stuff 
MyApp.foo = { 

    init: function(){ 
     var self = this; 

     // this page happens to use the same widget3 and configuration present 
     // in MyApp.index. this is where things can get sloppy and unmaintainable 
     // really quickly. 
     require(['js/application.index.js'], function(){ 
      $(function(){ 
       MyApp.index.widget3($("#bar")); 
      }); 
     }); 

     // page "foo" has three subpages (or actions) and require 
     // their own logic. url convention: /foo/subpage1/ 
     // init whichever page we're on... 
     switch(self.querystring.subpage){ 
      case "subpage1": 
       self.subpage1.init(); 
       break; 
      case "subpage2": 
       self.subpage2.init(); 
       break; 
      case "subpage3": 
       self.subpage3.init(); 
       break; 
     } 
    }, 

    subpage1: function(){ 
     init: function(){ 
      var self = this; 

      // once the DOM is ready init dialog. 
      $(function(){ 
       self.dialog($("#openDialog")); 
      }); 
     }, 

     dialog: function(element){ 
      element.bind("click", function(){ 
       $('<div></div>').dialog({ 
        open: function(){ 

         // see what i'm doing here? 
         MyApp.rebindable(); 

         // maybe more bindings specific to this 
         // dialog here 
        } 
       }); 
      }); 
     } 
    }, 

    subpage2: function(){ 
     init: function(){ 
     } 
    }, 

    subpage3: function(){ 
     init: function(){ 
     } 
    } 
}; 
+1

本週我正在考慮這個問題。我最近加入了一個項目團隊作爲主要的JavaScript編碼器。該項目已經進行了一年多了,所以他們已經在各地都有javascript了。他們已經積累了大量的JavaScript庫,並且努力使用命名空間,但是很多頁面都有自定義的javascript,不僅是用頁面和用戶控件編寫的,還有一些是基於頁面或用戶控制數據生成的。我有理解這一切的艱鉅任務,並試圖儘可能標準化。 – Silkster 2010-09-08 14:41:30

+0

(續)你的方法是一個好的開始,但我可以看到它無法維護的地方。也許你可以設計一個配置頁面和控件的方法,以便不必使用許多switch語句。您也可以查看Claypool(http://www.claypooljs.com/),看看是否有幫助。 – Silkster 2010-09-08 14:46:40

+1

@Silkster鏈接被破壞:http://http//www.claypooljs.com/ – 2011-05-19 11:38:29

回答

12

要幫我回答您的具體問題,請允許我談談JavaScriptMVC的功能一點:

控制器會改善你的jQuery的窗口小部件,以安裝/拆卸,可擴展的服務。

查看添加了可以構建到您的應用中的客戶端模板。

模型提取服務/數據層,如果您的服務器發生變化,最小化和本地化JS更改。

Steal執行依賴關係管理,壓縮和代碼清理。它甚至會將所有腳本都用在所有頁面上,找出共享依賴關係,並將腳本組合成最佳有效載荷。

FuncUnit使測試您的應用程序儘可能簡單。

DocumentJS ......好文件代碼

現在您的具體問題:

如何處理在多個地方使用的邏輯?

我使用StealJS的依賴管理系統將我需要的功能加載到我的頁面中。對於一定規模的應用程序來說,依賴管理是絕對必要的。如果您能夠輕鬆構建它,RequireJS是一個不錯的選擇。

你如何組織頁面特定代碼

頁特定的代碼應儘可能小。它通常涉及加載依賴和一個「MainController」。該主控制器配置該頁面以遵守該頁面的功能/業務要求。你怎麼停止寫入相同的模式

好吧,我建議使用具有發展穩定模式的框架

App.Controllers.Main 

:它通常的命名空間類似。此外,儘可能保持模塊/插件/小部件儘可能小(並且可測試)。這將使這些部件更不可能改變。

最後....

看來之間的最大斗爭張力:

  • 共享功能
  • 多頁
  • 及時加載時間

所以採摘一個可靠的依賴管理工具是sup呃關鍵。 StealJS可以幫助您獲得最佳的加載時間,但由於頁面數量較多,您必須偏離JavaScriptMVC的標準文件夾組織。

RequireJS更加靈活,但您將不得不加載大量文件。這不僅會很慢,這會開始讓你創建很多大JS組織不大的大JS文件。

如果您對加載時間感到滿意,並覺得它們不會讓您將代碼壓縮到不屬於它的文件中,那麼您當前的解決方案看起來好像可行。

我認爲可維護開發的祕訣在於您的系統/框架如何輕鬆地隔離問題。將應用分解成儘可能最小的部分非常重要。另外,你應該測試這些部分。人們通過思考他們的頁面功能而受到側面追蹤。但是爲了真正擴展開發,你真的需要一些東西,使你可以將應用分解成小部分,輕鬆加載這些部分,並以某種方式讓應用在生產中保持快速運行。

+2

澄清:RequireJS可能會在開發過程中加載單個文件,但它有一個優化工具(http://requirejs.org/docs/optimization.html),允許您將模塊優化/分組爲一小組HTTP請求。優化工具也可用於開發中,它有一種方法可以僅排除某些您可能想要自行調試的模塊。 – jrburke 2010-09-09 00:08:58

+0

是的,這是我的錯誤。 RequireJS可以組合所有腳本。但我不認爲它可以做幾個項目之間的竊取自動破解。 – 2010-09-26 20:11:44

+0

「主控制器配置頁面以遵守該頁面的功能/業務要求。」所以控制器是這裏邏輯的主要驅動器,而不是服務器端編程,其中業務邏輯被鼓勵保留在模型中? – 2013-02-16 06:29:15