这是一个很好的问题.主干是很棒的,因为它没有做任何假设,但它确实意味着您必须(决定如何)自己实现这样的事情.看过我自己的东西后,我发现我(有点)混合使用了场景1和场景2.我认为不存在第四个神奇的场景,因为足够简单,你在场景1和场景2中所做的一切都必须要做.
我想用一个例子来解释我喜欢如何处理它是最容易的.假设我将这个简单的页面拆分为指定的视图:
假设HTML在呈现后是这样的:
<div id="parent">
<div id="name">Person: Kevin Peel</div>
<div id="info">
First name: <span class="first_name">Kevin</span><br />
Last name: <span class="last_name">Peel</span><br />
</div>
<div>Phone Numbers:</div>
<div id="phone_numbers">
<div>#1: 123-456-7890</div>
<div>#2: 456-789-0123</div>
</div>
</div>
希望HTML与图表的匹配非常明显.
ParentView
包含两个子视图InfoView
和PhoneListView
以及几个额外的div,其中#name
需要在某个点上设置.PhoneListView
保存它自己的子视图,由PhoneView
个条目组成的array.
那么继续你的实际问题吧.我根据视图类型以不同的方式处理初始化和呈现.我把我的视图分成两类,Parent
个视图和Child
个视图.
它们之间的区别很简单,Parent
个视图包含子视图,而Child
个视图不包含子视图.在我的例子中,ParentView
和PhoneListView
是Parent
个视图,而InfoView
和PhoneView
个条目是Child
个视图.
就像我之前提到的,这两个类别之间最大的区别是它们被允许渲染的时间.在一个完美的世界里,我希望Parent
个视图只渲染一次.模型更改时,由其子视图处理任何重新渲染.Child
个视图,另一方面,我允许随时重新渲染它们,因为它们没有任何其他依赖它们的视图.
更详细地说,对于Parent
个视图,我希望我的initialize
函数可以做一些事情:
- 初始化我自己的视图
- 渲染我自己的视图
- 创建并初始化所有子视图.
- 在我的视图中 for each 子视图指定一个元素(例如,
InfoView
将被指定为#info
).
第一步非常简单.
第2步(呈现)已经完成,这样在我try 分配子视图所依赖的任何元素之前,它们就已经存在了.通过这样做,我知道所有的子events
都将被正确设置,并且我可以任意多次地重新渲染它们的块,而不必担心必须重新委托任何东西.我在这里实际上没有render
个子元素的观点,我允许他们在他们自己的initialization
中这样做.
实际上,在创建子视图时,在我传入el
的同时处理步骤3和4.我想在这里传递一个元素,因为我觉得家长应该决定子元素在自己的视图中可以把内容放在哪里.
对于渲染,我试着在Parent
个视图中保持相当简单.我希望render
函数只渲染父视图.没有事件委托,没有呈现子视图,什么都没有.只是一个简单的渲染.
但有时候这并不总是有效的.例如,在我上面的示例中,当模型中的名称更改时,#name
元素将需要随时更新.然而,这个块是ParentView
模板的一部分,不是由专用的Child
视图处理的,所以我解决了这个问题.我将创建某种subRender
函数,only替换#name
元素的内容,而不必丢弃整个#parent
元素.这看起来像是一种黑客行为,但我真的发现它比担心重新呈现整个DOM和重新连接元素之类的事情更有效.如果我真的想把它弄干净,我会创建一个新的Child
视图(类似于InfoView
),来处理#name
块.
现在,对于Child
个视图,initialization
相当于Parent
个视图,只是没有再创建Child
个视图.因此:
- 初始化我的视图
- 安装程序将监听我关心的模型的任何更改
- 渲染我的视图
Child
视图渲染也很简单,只需渲染并设置my el
的内容.再次强调,不要干扰授权或诸如此类的事情.
下面是我的ParentView
可能是什么样子的一些示例代码:
var ParentView = Backbone.View.extend({
el: "#parent",
initialize: function() {
// Step 1, (init) I want to know anytime the name changes
this.model.bind("change:first_name", this.subRender, this);
this.model.bind("change:last_name", this.subRender, this);
// Step 2, render my own view
this.render();
// Step 3/4, create the children and assign elements
this.infoView = new InfoView({el: "#info", model: this.model});
this.phoneListView = new PhoneListView({el: "#phone_numbers", model: this.model});
},
render: function() {
// Render my template
this.$el.html(this.template());
// Render the name
this.subRender();
},
subRender: function() {
// Set our name block and only our name block
$("#name").html("Person: " + this.model.first_name + " " + this.model.last_name);
}
});
你可以在这里看到我对subRender
的实现.通过将更改绑定到subRender
而不是render
,我不必担心爆炸和重建整个街区.
下面是InfoView
块的示例代码:
var InfoView = Backbone.View.extend({
initialize: function() {
// I want to re-render on changes
this.model.bind("change", this.render, this);
// Render
this.render();
},
render: function() {
// Just render my template
this.$el.html(this.template());
}
});
绑定是这里的重要部分.通过绑定到我的模型,我永远不用担心自己手动拨打render
.如果模型发生更改,该块将在不影响任何其他视图的情况下重新渲染自身.
PhoneListView
将类似于ParentView
,您只需要在initialization
和render
函数中多一点逻辑来处理集合.如何处理集合实际上取决于您,但您至少需要监听集合事件并决定如何呈现(追加/删除,或者只是重新呈现整个挡路).我个人喜欢添加新视图和删除旧视图,而不是重新渲染整个视图.
PhoneView
和InfoView
几乎是一样的,只听它关心的型号变化.
希望这对我有所帮助,如果有什么让人困惑或不够详细,请告诉我.