2014-11-03 101 views
1

我正在努力想出一個乾淨,堅實的方式來組織我的Backbone應用程序。我使用Requirejs,Handlebars和Requirejs Text插件來動態加載HTML視圖。爲簡單起見,我們只說,該網站具有以下頁面:骨幹項目組織

主頁:顯示器產品

集合

關於:靜態頁面

帳戶:包含帳戶信息。購買的產品可以進行各種更新。許多功能。有製表符導航到不同的部分。

因此,我正在尋找將新頁面加載到div('.backbone-view')的SPA。我應該使用一個通用的AppView和el:$('。backbone-view'),當路由發生變化時調用它,然後加載相應的模板?或者我應該爲每個頁面(homeView,aboutView,accountView)都有一個視圖,所有的都設置爲backbone-view?

除此之外......我需要一個除產品以外的任何模型嗎?對於關於頁面的靜態,我只是在html模板中加載,就是這樣。但對於產品,我需要調用產品集合,它呈現每個產品視圖,每個產品視圖都與產品模型相關聯。沒關係......但我在哪裏初始化這些產品結構?當我路由到主頁時,我在那裏做?我有這樣的僞代碼:

routes: { 
     '': 'home', 
     'about': 'about', 
     'my-account': 'myAccount', 
     '*default': 'home' 
    }, 

    'home': function() { 
     // Grab template for home page 

     // Load up products 

     // Replace $('.backbone-view') with home page template populated with products 
    }, 

    'about': function() { 
     // Grab about template and replace $('.backbone-view') with its contents 
    }, 

    'myAccount': function() { 
     MIND EXPLOSION 
    } 

我覺得一個大問題是,我不是意見的目的明確的......他們能簡單地用於頁面過渡,還是應該始終有一個模型附在他們身上?如果前者,我至少需要一個AppView,然後每個頁面都需要View,對吧?我迷失在哪裏我會委派每一步......所以任何幫助表示讚賞。

感謝您的幫助!

回答

4

下面是在處理非常大的主幹應用程序後的一些技巧。這不是窮盡的或最終..

分成兩個目錄

  1. 服務器目錄e.g server/
  2. 可公開訪問的目錄如www/

此外,當您運行構建的任務,將構建應用程序到分發版本爲build/dist/目錄。可能使用Gulp或Grunt。

擴展骨幹

你的整個應用程序將包括:

  • 查看&子視圖
  • 路由器&子路由器
  • 模型
  • 集合

您應該首先擴展Backbone類,即使它們是空的。最有用的兩個擴展是:

  • 子視圖(視圖可以有更多的意見,這時候你刪除父視圖得到清理一個views對象/功能)。模型和集合稱爲modelcollection會自動傳遞給子視圖。
  • 子路由器(這是不錯的路由邏輯模塊文件夾內的每個模塊)

用POD架構

如圍繞您的應用程序獨立模塊如:

www/app/modules/home/router.js < - 子路由器,調用modules.js中的方法
www/app/modules/home/module.js < - 準備端點 - 更改佈局,初始化視圖小號&模型等
www/app/modules/home/views/...所有視圖(可以有子文件夾太)
www/app/modules/home/templates/
www/app/modules/home/models/
www/app/modules/home/collections

開始看到你的應用程序中的觀點條款和子視圖

一個頁面沒有按」 t只包含一個視圖。它可能會有一個特殊的「佈局」視圖,內部會有很多視圖 - 一個將頁面分成兩半,一個分頁顯示每個頁面的內部更多視圖,一個顯示內部大量子視圖的窗體對於每個表單元素和消息等等

您可以開始思考視圖DOM樹的陰影和邏輯上的劃分 - 任何你認爲在你的頁面上可重用的使它成爲一個包(它自己的視圖和模型/集合如果它需要它們的話)。

模型用於對數據執行的任何數據和任何邏輯,如果某個視圖顯示的是server/api/database中的任何內容,則它通常會傳遞給視圖,該視圖將全部或部分模型屬性傳遞給模板。

如果顯示信息的項目在列表中,則集合將管理每個項目的每個模型。

配車型

待辦事項通信如果你發現自己想要從某種看法傳達給另一種觀點認爲,使用共享的模式。視圖應該儘可能地分離(它不應該知道它是父項)。

有一個應用程序狀態

創建一個名爲屆時AppState模型中使用觸發器和監聽整個應用廣泛的交流。

有一個包文件夾(可選)

每當您在您的應用程序,你覺得可以重複使用遇到的東西,甚至在其他以後的應用程式創建一個包。這些通常會被託管在自己的git倉庫中,您可以使用package.json或命令行將它們拖放到項目中。

有你在那裏延伸應用內間東西

的文件夾具有用於其通過多個應用程序模塊所消耗的擴展文件夾 - 例如你的骨幹擴展可能會在這裏。或者,如果您爲表單創建了一個包,但又想專門爲這個應用做點什麼,那麼請在此處進行擴展。

例如 www/app/extensions/view.js
www/app/extensions/model.js
www/app/extensions/collection.js
www/app/extensions/buttons/link.js //擴展從一個 「按鈕」 包的鏈路圖。

資產

我之所以會在公衆www/文件夾中的app/文件夾,這樣我也可以有一個資產的文件夾中有字體和圖像等:

www/assets/css
www/assets/images

注意:也許您想嘗試將資源保留在模塊文件夾中(與pod體系結構內聯)。我以前沒有這樣做,但值得考慮。

的index.html

通常,如果你正在使用CommonJS的或AMD您的index.html也只是樣板,沒有實際的DOM元素,你會在那裏有一個入口js文件一個電話。由於CommonJS的有編制這也只是像<script src="/app.js"></script>但AMD會更喜歡:

<!--IF NOT BUILD--> 
<script data-main="/app/config" src="/packages/require.js"></script> 
<!--ELSE 
<script src="/app.js"></script> 
--> 

所以在開發運行時(非編譯)RequireJS將加載app/config.js但在構建整個應用程序會在app.js。有各種各樣的Grunt/Gulp構建任務,它們將爲你做上述事情(顯然條件語法就是這樣構成的)。

佈局

我會創造一個extensions/layout.js延伸extensions/view.js,這將是一個簡單的擴展,它可以像正常子視圖(如頁眉和頁腳),而且,我可以附加任何特殊的子視圖查看(對於身體子視圖)例如像setContentView(view)這樣的方法。

我可能會創建一個名爲佈局的模塊,並在那裏有一個目錄modules/layout/default它有一個頁眉和頁腳子視圖的視圖。然後到達指數路線將流向是這樣的:

app/router.js => app/modules/home/router.js => app/modules/home/[email protected] => setContentView(view from app/modules/home/views/index.js)"

路由

我想有一個應用程序路由器位於例如www/app/router.js這可能有一些特殊的途徑,但將在很大程度上只是一個對象,在子路由器指出subroute:

subRouters: { 
    'store-locator': StoreLocatorRouter, 
    myaccount: MyAccountRouter, 
    sitemap: SitemapRouter 
} 

我會做這個可以通過與像(注意延長正常的骨幹路由器在擴展需要調用initSubRoutersinitialize) -

define([ 
    'underscore', 
    'backbone' 
], 
function(_, Backbone) { 

    'use strict'; 

    /** 
    * Extended Backbone Boilerplate Router 
    * @class extensions/router 
    * @extends backbone/view 
    */ 
    var Router = Backbone.Router.extend(
     /** @lends extensions/router.prototype */ 
     { 

     /** 
     * Holds reference to sub-routers 
     * @type {Object} 
     */ 
     subRouters: {}, 

     /** 
     * Adds sub-routing 
     * based on https://gist.github.com/1235317 
     * @param {String} prefix The string to be prefixed to the route values 
     */ 
     constructor: function(options) { 
      if (!options) { 
       options = {}; 
      } 

      var routes = {}, prefix = options.prefix; 

      if (prefix) { 
       // Ensure prefixes have exactly one trailing slash 
       prefix.replace(/\/*$/, '/'); 
      } else { 
       // Prefix is optional, set to empty string if not passed 
       prefix = ''; 
      } 

      if (prefix) { 
       // Every route needs to be prefixed 
       _.each(this.routes, function(callback, path) { 
        if (path) { 
         routes[prefix + '/' + path] = callback; 
        } else { 
         // If the path is "" just set to prefix, this is to comply 
         // with how Backbone expects base paths to look gallery vs gallery/ 
         routes[prefix + '(/)'] = callback; 
        } 
       }); 

       // Must override with prefixed routes 
       this.routes = routes; 
      } 

      // .navigate needs subrouter prefix 
      this.prefix = prefix; 

      // Required to have Backbone set up routes 
      Backbone.Router.prototype.constructor.apply(this, arguments); 
     }, 

     /** 
     * Sets up 'beforeRoute' event. 
     */ 
     initialize: function() { 
      // This is a round about way of adding a beforeRoute event and must 
      // happen before any other routes are added. 
      Backbone.history.route({ 
       test: this.beforeRoute 
      }, function() {}); 
     }, 

     /** 
     * Called before routes. 
     * @return {Boolean} false This ensures the 'route' is disabled. 
     */ 
     beforeRoute: function() { 
      Backbone.history.trigger('beforeRoute'); 
      return false; 
     }, 

     /** 
     * Adds prefix to navigation routes 
     * @param {String} route Non-prefixed route 
     * @param {Object} options Passed through to Backbone.router.navigate 
     */ 
     navigate: function(route, options) { 
      if (route.substr(0, 1) !== '/' && route.indexOf(this.prefix.substr(0, 
       this.prefix.length - 1)) !== 0) { 
       route = this.prefix + route; 
      } 
      Backbone.Router.prototype.navigate.call(this, route, options); 
     }, 

     /** 
     * Initializes sub-routers defined in `this.subRouters` 
     */ 
     initSubRouters: function() { 
      _.each(this.subRouters, function(Router, name) { 
       this[name] = new Router({ 
        prefix: name 
       }); 
      }, this); 
     } 

    }); 

    return Router; 
}); 

project layout

app layout

+0

很好的答案,感謝Dominic!我已經開始用這個模板重建我的項目,並且它已經更加有組織。我只是遇到了一些後續問題:如果我的每個頁面上都顯示了頭部的部分模板(但也基於其他因素動態顯示),我應該將它放在擴展文件夾中,因爲它是由每個模塊?另外,當我點擊具有視圖和子視圖的路線時,你能解釋應用程序流程嗎?像,調用主視圖的路由器 - >方法 - >調用子視圖的主視圖內的方法...?謝謝! – 2014-11-03 17:53:37

+0

@ user918065 Np我已經添加了一些更多信息。我只是模糊地摸到了一些東西,所以隨時問問你是否卡住了。很可能你並不需要擔心自己的軟件倉庫和其他東西。 – 2014-11-03 18:13:22

+0

謝謝!快速的問題:當我通過鏈接導航到他們時,我的子路由器正在工作,但是當我直接導航到URL時,我的子路由器不工作。在這種情況下,子路由器永遠不會被調用... – 2014-11-03 22:56:20