我目前正在开发一个基于backbone.js构建的大型网络应用程序,并且在组织,“僵尸”等方面遇到了很多问题。所以我决定做一个主要的代码重构。我已经编写了一堆辅助函数来处理“僵尸”;但是,我想从一开始就为代码创建一个很好的结构/组织。我没有在大型的backbone.js组织上找到很多很棒的教程/例子,所以我从头开始,想看看我是否可以就我开始的地方得到一些意见。
我显然在全局命名空间中设置了我的代码;但我也想保持这个命名空间相当干净。我的主要app.js使类文件本身与全局命名空间分开;您可以使用reg()函数注册一个类(以便可以实例化它),而inst()函数可以从classes数组中实例化一个类。因此,除了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);
在模型,集合,路由器和视图中,我像往常一样工作,但是然后需要在文件的末尾注册该类,以便可以在以后实例化它(不会使命名空间混乱): p>
MyApp.reg('Models', 'App', Model);
这看起来像组织代码的不必要的方法吗?其他人是否有更好的例子来说明如何使用许多路由器,集合,模型和视图组织真正的大型项目?
答案 0 :(得分:32)
我最近参与了一个名为GapVis(code here,rendered content here)的Backbone项目。我不知道它是否“非常大”,但它是大而且相对复杂 - 24个视图类,5个路由器等等。虽然我不知道我的所有方法都是相关。你可以在main app.js file的长篇介绍评论中看到我的一些想法。一些关键的架构选择:
我有一个单独的State
模型,它包含所有当前状态信息 - 当前视图,我们正在查看的模型ID等等。每个需要修改应用程序状态的视图都通过设置来完成State
上的属性,每个需要响应状态的视图都会侦听该模型的事件。对于修改状态和更新的视图尤其如此 - events
中的UI事件处理程序永远不会重新呈现视图,而是通过将呈现函数绑定到状态来完成。这种模式确实有助于保持视图彼此分离 - 视图从不在另一个视图上调用方法。
我的路由器被视为专门的视图 - 它们通过更新状态来响应UI事件(即键入URL),并通过更新UI(即更改URL)来响应状态更改。 p>
我做了几件类似于你提议的事情。我的命名空间有一个类似于你的init
函数,以及一个settings
常量对象。但我也将大部分模型和视图类放在命名空间中,因为我需要在多个文件中引用它们。
我为我的路由器使用注册系统,并将其视为我的视图,这是保持“主”类(AppRouter
和AppView
)的一种好方法意识到每一个观点。然而,在AppView
案例中,结果显示子视图的顺序非常重要,因此我最终对这些类进行了硬编码。
我很难说这是做事的“正确”方式,但它对我有用。我希望这很有帮助 - 我也很难找到使用Backbone的大型项目的可见源代码示例,并且在我进行过程中不得不解决大部分问题。
答案 1 :(得分:13)
这2个资源帮助我在坚实的基础上设置我的骨干应用程序:
答案 2 :(得分: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.
}
我希望我的所有观点都有,我把它放在基本视图中。我的控制器将在它创建的任何新视图(渲染后)上调用registerEvents
,并在视图杀死之前调用unregisterEvents
。并非所有视图都有这两个额外的方法,所以它首先检查是否存在。
不要忘记所有视图都内置了this.el.remove();
。这不仅会杀死视图容器元素,还会取消绑定附加到它的所有事件。根据您通过控制器创建视图的方式,您可能实际上不想杀死该元素并执行this.el.unbind()而不是取消绑定所有事件。
答案 3 :(得分:5)
事实上,以不同的方式有不同方式的优点和缺点。最重要的是找到一种合适的文件组织方式。以下是我目前正在进行的项目的组织。这种方式的重点是将相同的模块相关文件放在一个文件夹中。例如:people模块,这个模块的所有文件都放在modules / base / people目录下。更新和维护这个模块后,只需要关注这个目录中的文件就行了,不会影响目录外的文件,提高了可维护性。
我希望我的回答可以给你一些帮助,我希望你有一些宝贵的建议。