2014-11-03 103 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