我正在构建我的第一个Backbone.js应用程序,我很担心我应该给予或隐藏我的视图的责任。
在我的示例中,我正在构建一个Rich UI表(类似于YUI的datagrid),它是从Collection动态生成的。在我的应用程序中,我称之为“AppTable”。在我对MVC的理解中,我会想到会有某种AppTable控制器找到正确的Collection,抓取一个“哑”视图并传递给View所需呈现的任何信息。在这个场景中,View只会提供提供给它的数据并相应地修改DOM,甚至可以填充模板或附加事件监听器。
Backbone似乎不再考虑让控制器在View和Collection之间进行调解。相反,View通过对Collection的引用进行初始化,并且View有责任自行更新。
我是否正确理解了这种架构?
假设我这样做,我的问题就变成了,当我的View需要做的越来越多时会发生什么?例如,我想要列排序,拖放行,分页,搜索,表控制链接(如新建,复制,删除行等)等等。如果我们坚持使用View直接连接到Collection的“智能”View范例,上面的函数是否会附加到View对象?
通过这个思考,我可以看到View从一个简单的表包装器变成了一个非常混乱的野兽,附带了很多功能。那么,View在这种情况下真的是一个控制器吗?
答案 0 :(得分:22)
您对架构的理解是正确的。 Backbone不承认传统MVC意义上的“控制器”的概念。 (实际上,Backbone实际上有一个名为Controller
的对象,但它已被重命名为Router
以更准确地描述它的作用。)
您列出的功能(拖放,删除行,排序等)都属于视图。视图描述您看到的内容并响应用户输入。任何涉及事件(点击,按键,提交等)的内容都会进入视图。但是你的观点永远不应该实际操纵数据;这应该由model
来完成。您认为视图就像控制器一样是正确的,因为它打包数据并将其发送到模型,然后模型将适当地验证/设置/保存。一旦发生这些操作,视图将重新呈现为表示模型内新数据的版本。
需要注意的一点是:您的观点不应过于依赖DOM。 Backbone惯例是具有与视图绑定的顶级DOM元素(例如,表单或div),然后仅处理其子元素。这是合适的;一般情况下,视图中的“从此div中删除此链接”之类的内容不是。如果您发现自己的观点变得笨拙,那么您很可能需要将其分解为子视图,每个子视图都将其各自的行为作为整体的组成部分。
答案 1 :(得分:10)
我对此更新的想法如下: 我认为Josh给出了一个很好的答案,但是,根据我的经验,构建一些Backbone应用程序,即使是中等复杂度的应用程序也需要一个单独的控制器类。
澄清我对控制器的意思:模型(或路由器)与创建和实例化新视图类并在旧视图类上杀死(和取消注册事件)的视图之间的功能。对于许多视图,此功能可能相同(因此可能不需要视图和控制器之间的直接一对一关系),但有时需要传入模型或其他额外的值。
现在,我只有一个带有一些if语句的控制器,用于为我构建的大多数应用程序添加一些独特的数据到某些视图但是我正在设置一个架构,它将检查是否有一个独特的该视图存在控制器,否则它将回退到标准控制器。没什么特别的,但应该做好这份工作。
更新在构建Backbone应用程序六个月后,我意识到路由器可以像视图一样拆分和扩展。 (杜?)
马上就知道我知道我的所有观点都需要的功能基础视图。同样,我会为每个部分制作基本视图,例如“profile”页面或“收件箱”页面,我知道这些页面都将使用相同的功能。对于我来说,这对路由器来说并不是那么清楚,但之前的名称“控制器”暗示了这一点。
大多数人(就像我在网上看到的每个Backbone示例一样)只使用一个单片路由器实例来处理所有路由,但实际上你可以将1到1奇偶校验路由器用于视图,或者在我的case,用于检查用户身份验证等的基本路由器,然后是每个主要部分的一个。这样,如果您需要在页面加载时将某些模型或集合传递给路由器,则不需要将代码添加到一个单片路由器,而是为该视图拉出唯一的路由器。我发现这比创建一个单独的控制器类要好。基础路由器可以负责最后实例化的视图等,因此您可以在实例化新视图之前终止最后一个视图。
TLDR:使用多个路由器作为控制器。我相信这就是他们的意思,而且运作良好。
答案 2 :(得分:8)
在尝试绘制单页应用时,我遇到了同样的语义问题。最后,我认为Backbone使用了错误的名称。
当您在浏览器中查看Backbone应用程序时,View实际上根本不是视图,其el
成员是视图。 Backbone.View
可以是视图控制器,也可能更准确地说是演示者。
一些支持证据:
您从未在屏幕上看到Backbone.View
,它始终是应用于DOM的el
或$el
Backbone.View
没有收到用户输入,DOM元素接收输入,事件通过“view”的events
哈希委派
BackBone.View
管理模型或集合更改并将这些更改转换为哑视图(DOM)元素,然后将它们应用于实际视图,例如this.$el.append('<p>Cats!')
我认为Backbone.Presenter
会是一个更好的名称,但我也可以看到有一个前Backbone.Controller
的历史问题以及重新命名的工作量。
我为我的最新项目确定了以下结构:
应用程序控制器,从Backbone.View
扩展,绑定到body元素
用于缓存从服务器检索的数据的多个模型集合
Backbone.Router
将路线更改转换为Backbone事件并自行触发
许多app控制器方法,用于处理app控制器侦听的路由器事件
app控制器方法准备任何需要的模型,然后启动演示者(从Backbone.View
扩展)并将其附加到body元素
所有这些部件均由app控制器启动并拥有。演示者不知道他们在页面上的原因或位置,只关心他们自己的DOM元素以及他们从this.model
收到的更改。
答案 3 :(得分:5)
查看骨干文档的这一部分
http://documentcloud.github.com/backbone/#FAQ-tim-toady
模型和视图之间的引用可以通过多种方式处理。一些 人们喜欢有直接指针,其中视图对应1:1 models(model.view和view.model)。其他人更喜欢中间人 “控制器”对象,协调组织的创建和组织 查看层次结构。其他人仍然喜欢这种方法,而且 总是触发事件而不是直接调用方法。所有这些 风格很好。
所以,骨干不会为你做出这个决定。
我有一个非常相似的用例(带有分页,排序,实时过滤的表格,带有客户端验证的表格,主要细节关系等)。
在我的情况下,我首先开始使用路由器,就像控制器一样,很快我的代码就变得有点乱了。
所以我完全删除了路由器(我稍后会将它们添加回来,但只是作为一个补充)并创建了我自己的控制器(实际上它是一个演示者)。它只是一个javascript类,Backbone.extend支持处理继承。
这个想法是视图接收显示自身所需的所有数据(模型,集合和应该解析它的el),在dom事件上设置监听器,然后执行控制器方法。它从不直接修改数据,也不与其他视图交互,它告诉控制器这样做。
视图可以包含子视图,在这种情况下,子视图只与父视图交互,或直接与控制器交互。
到目前为止它似乎有效,但无论如何事情并不像我预期的那样简单......
我希望在接下来的几天内发布它。
答案 4 :(得分:3)
与其他答案的不同之处在于,仅仅因为您使用的是Backbone框架,这并不意味着您的整个代码库必须包含在Backbone类中。
就我个人而言,我的控制器是“原始”Javascript和Backbone路由的合并,我从来没有使用Views作为控制逻辑。恕我直言的观点是......嗯,查看逻辑,特别是包装元素。如果您正在使用视图来处理任何不直接连接到HTML元素的视图,那么您(再次,恕我直言)会做错事。
Backbone很棒,但这并不意味着它可以应用于所有东西。