2011-11-03 78 views
61

我目前正在构建一个基于backbone.js的大型web应用程序,并且在组织,“僵尸”等方面遇到很多问题,所以我决定做一个主要的代码重构。我已经写了一些帮助函数来处理“僵尸”;不过,我想从头开始,并为代码创建一个很好的结构/组织。我还没有找到关于大规模backbone.js组织的很多很棒的教程/例子,所以我从头开始着手,希望看看我能否从我开始的地方得到一些意见。Large backbone.js web应用程序组织

我明显在全局命名空间中设置了我的代码;但我也想保持这个名称空间相当干净。我的主要app.js保持类文件本身与全局名称空间分开;你可以使用reg()函数注册一个类(以便它可以被实例化),而inst()函数实例化类数组中的一个类。因此,除了3种方法中,MyApp的命名空间只有路由器,模型和视图:

var MyApp = (function() { 

    var classes = { 
     Routers: {}, 
     Collections: {}, 
     Models: {}, 
     Views: {} 
    }; 

    methods = { 

     init: function() { 
      MyApp.Router = MyApp.inst('Routers', 'App'); 
      MyApp.Model = MyApp.inst('Models', 'App'); 
      MyApp.View = MyApp.inst('Views', 'App'); 
      Backbone.history.start(); 
     }, 

     reg: function (type, name, C) { 
      classes[type][name] = C; 
     }, 

     inst: function (type, C, attrs) { 
      return new classes[type][C](attrs || {}); 
     } 

    }; 

    return methods; 

}()); 

$(MyApp.init); 

中的Models,集合,路由器和意见,我像往常一样工作,但随后需要在年底来注册类因此,它可以在以后被实例化的文件(而不会弄乱命名空间)与:

MyApp.reg('Models', 'App', Model); 

这是否看起来像组织代码不必要的方法吗?其他人是否有更好的例子来说明如何用许多路由器,集合,模型和视图组织真正的大型项目?

+0

也许http://codereview.stackexchange.com更适合这样的问题 - 但使我们更新(后链接);) – sled

+0

可以在不同的模块中的组织代码。您可以查看https://github.com/juggy/backbone-module小型自我推销:p – Julien

回答

32

我最近在一个名为GapVis的Backbone项目上工作(code here,rendered content here)。我不知道它是否“真的很大”,但它是大而且相对复杂的 - 24个视图类,5个路由器等。可能值得一看,尽管我不知道我的所有方法都是相关。您可以在我的main app.js file的长篇介绍评论中看到我的一些想法。几个关键的架构选择:

  • 我有一个单State模型保存所有当前状态信息 - 目前看来,我们看到的是什么型号IDS等需要修改应用程序的状态是否每个视图它通过设置State上的属性,每个需要响应状态的视图监听该模型的事件。对于修改状态和更新的视图甚至是这样 - events中的UI事件处理程序从不重新呈现视图,而是通过将呈现函数绑定到状态来完成此操作。这种模式确实有助于保持视图彼此分离 - 视图从不会在另一视图上调用方法。

  • 我的路由器被视为专门的视图 - 它们通过更新状态来响应UI事件(即键入URL),并通过更新UI(即更改URL)响应状态更改。

  • 我做了几件与你的建议类似的事情。我的命名空间有一个类似于你的init函数,以及一个用于常量的settings对象。但是我将大部分模型和视图类放在名称空间中,因为我需要在多个文件中引用它们。

  • 我对我的路由器使用了一个注册系统,并将其视为一个用于我的视图,这是让“主”类(AppRouterAppView)不必知道每个视图的好方法。然而,在AppView的情况下,结果是子视图的顺序很重要,所以我最终对这些类进行了硬编码。

我几乎不会说这是做事的“正确”方式,但它对我有效。我希望这会有所帮助 - 我也很难找到使用Backbone的大型项目的可见源示例,并且在我进行时必须解决大部分此类问题。

+0

感谢您提供的所有信息;我很高兴我们都有类似的思维过程 - 至少证明我不是疯狂的哈哈。我绝对喜欢你关于更频繁使用事件和使用路由器作为简单的“URL视图”的想法。 我想我仍然不确定我的所有类的注册系统是否值得...它不会混淆全局命名空间,但是这很重要吗?是否存在与使用类文件混淆MyApp命名空间相关的性能问题? – user527480

+0

我认为如果你的主要目标是保持命名空间的清洁 - 我没有发现任何性能问题,那么我认为你可能会推翻这一部分,问题更多的是关于代码组织。使用'var MyClass'而不是'ns.MyClass'有一定的压缩优势,它通常不会被压缩,但它很小,而且您仍然使用字符串名称。 – nrabinowitz

5

我命名空间类似于你在做什么(至少在部分班级)和所有我的模型,视图和控制器是这样的:

的意见/ blocks.js:

(function(cn){ 
    cn.classes.views.blocks = cn.classes.views.base.extend({ 

     events: {}, 

     blocksTemplate: cn.helpers.loadTemplate('tmpl_page_blocks'), 

     initialize: function(){ 
     }, 

     render: function(){ 
      $(this.el).html(this.blocksTemplate()); 
     }, 

     registerEvents: function(){}, 
     unregisterEvents: function(){} 
    }); 
})(companyname); 

我的JavaScript的命名空间看起来是这样,但我在它的每一个我建立一个新的应用程序时提高:

companyname:{                                             
    $: function(){},  <== Shortcut reference to document.getElementById                              
    appView: {},   <== Reference to instantiated AppView class.                               
    classes = {   <== Namespace for all custom Backbone classes.                               
    views : {},                                             
    models : {},                                            
    collections: {},                                           
    controllers : {},                                           
    Router: null                                            
    },                                               
    models: {},   <== Instantiated models.                                     
    controllers: {},  <== Instantiated controllers.                                   
    router: {},   <== Instantiated routers.                                    
    helpers: {},   <== Reusable helper platform methods.                                 
    currentView: {},  <== A reference to the current view so that we can destroy it.                           
    init: function(){} <== Bootstrap code, starts the app.                               
} 

什么我想我所有的意见都有,我把基本视图。我的控制器将在其创建的任何新视图(渲染之后)和unregisterEvents在其杀死视图之前调用registerEvents。并非所有的视图都有这两个额外的方法,所以它首先检查存在。

不要忘记所有视图都带有内置的this.el.remove();。它不仅会杀死视图容器元素,还会解除绑定的所有事件。根据您通过控制器创建视图的方式,您可能实际上不想杀死元素并执行this.el.unbind()而不是解除所有事件。

+0

在什么情况下,我不想杀死元素并执行this.el.unbind()? 我有一个方法关闭,做下一个: if(this.onClose) this.onClose(); $(this.el).remove(); $(this.el).unbind(); – ccsakuweb

+2

'.remove'自动执行'.unbind',所以你应该很好。 –

5

事实上,以不同的方式各有优点和不同的方式缺点。最重要的是找到合适的文件组织方式。以下是我目前正在进行的项目的组织结构。这样焦点将是相同的与模块相关的文件被放置在一个文件夹中。例如:人员模块,这个模块的所有文件都放在modules/base/people目录下。在更新和维护这个模块之后,只需要把重点放在这个目录下的文件就行了,不会影响目录之外的文件,并且提高了可维护性。

我希望我的回答能给你一些帮助,希望你能提供一些有价值的建议。

enter image description here

+1

我也有一个包含模块的文件夹,但我认为每个模块文件夹至少需要模型文件夹和视图文件夹 – ccsakuweb

+0

context.js的用途是什么?你有github吗? – alejandro