如何处理Backbone.js中的初始化和渲染子视图?
我有三种不同的方式来初始化和渲染视图及其子视图,并且每个视图都有不同的问题。 我很想知道是否有更好的解决所有问题的方法:
情景一:
在父项的初始化函数中初始化子项。 这样,并非所有东西都会在渲染过程中卡住,从而减少渲染阻塞。
initialize : function () {
//parent init stuff
this.child = new Child();
},
render : function () {
this.$el.html(this.template());
this.child.render().appendTo(this.$('.container-placeholder');
}
问题:
最大的问题是第二次调用父级渲染会删除所有子级事件绑定。 (这是因为jQuery的$.html()是如何工作的。)这可以通过调用this.child.delegateEvents().render().appendTo(this.$el);来缓解this.child.delegateEvents().render().appendTo(this.$el); 相反,但第一次,也是最常见的情况是,你不必要地做更多的工作。
通过追加孩子,您可以强制渲染功能掌握父母DOM结构的知识,以便获得您想要的顺序。 这意味着更改模板可能需要更新视图的渲染功能。
情景二:
初始化父对象的initialize()中的子对象,但不是附加,而是使用setElement().delegateEvents()将子对象设置为父对象模板中的一个元素。
initialize : function () {
//parent init stuff
this.child = new Child();
},
render : function () {
this.$el.html(this.template());
this.child.setElement(this.$('.placeholder-element')).delegateEvents().render();
}
问题:
delegateEvents() ,这对第一种情况下的后续调用而言只是必要的。 情景三:
请改为在父级的render()方法中初始化子级。
initialize : function () {
//parent init stuff
},
render : function () {
this.$el.html(this.template());
this.child = new Child();
this.child.appendTo($.('.container-placeholder').render();
}
问题:
这意味着渲染函数现在必须与所有初始化逻辑捆绑在一起。
如果我编辑其中一个子视图的状态,然后在父级上调用渲染,则会创建一个全新的子项,并且其当前状态的所有状态都将丢失。 这似乎也可能会导致内存泄漏的危险。
真的好奇,让你的家伙承担这一点。 你会使用哪种场景? 还是有第四个神奇的解决所有这些问题?
你有没有跟踪视图的渲染状态? 说一个renderedBefore标志? 似乎真的很难受。
这是一个很好的问题。 骨干是伟大的,因为它缺乏假设,但它的确意味着你必须(决定如何)自己实现这样的事情。 在看完我自己的东西之后,我发现我(种)混合使用场景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条目数组。
因此,你的实际问题。 我根据视图类型处理初始化和渲染。 我将视图分为两种类型,即Parent视图和Child视图。
它们之间的区别很简单, Parent视图可以保持子视图,而Child视图不会。 因此,在我的示例中, ParentView和PhoneListView是Parent视图,而InfoView和PhoneView条目是Child视图。
就像我之前提到的那样,这两个类别之间最大的区别在于它们允许渲染。 在完美的世界中,我希望Parent视图只能渲染一次。 当模型发生变化时,由他们的子视图来处理任何重新渲染。 另一方面, Child观点允许我们随时重新呈现,因为他们没有任何其他依赖他们的观点。
再详细一点,对于Parent视图,我喜欢我的initialize函数来做一些事情:
InfoView将被分配#info )。 第一步非常自我解释。
第2步,渲染完成后,我试图分配它们之前,子视图依赖的任何元素都已存在。 通过这样做,我知道所有的小孩events都会正确设置,并且我可以根据需要多次重新渲染自己的块,而不必担心需要重新分配任何东西。 我实际上并没有在这里render任何子视图,我允许他们在他们自己的initialization这样做。
步骤3和4是在为我传递的同时实际处理el在在创建子视图。 我喜欢在这里传递一个元素,因为我觉得父母应该确定孩子允许放置其内容的位置。
为了渲染,我尽量保持Parent视图非常简单。 我希望render函数只能渲染父视图。 没有事件代表团,没有渲染子视图,什么都没有。 只是一个简单的渲染。
有时这并不总是工作。 例如在上面的示例中,只要模型中的名称发生更改,就需要更新#name元素。 但是,此块是ParentView模板的一部分,并未由专用的Child视图处理,所以我解决了这个问题。 我将创建一些只替换#name元素内容的subRender函数,而不必垃圾整个#parent元素。 这可能看起来像一个黑客,但我真的发现它比不必担心重新渲染整个DOM并重新附加元素等方面效果更好。 如果我真的想把它弄干净,我会创建一个新的Child视图(类似于InfoView )来处理#name块。
现在对于Child视图, initialization与Parent视图非常相似,只是没有创建任何其他Child视图。 所以:
Child视图渲染也非常简单,只需渲染和设置我的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几乎完全相同,只是倾听其关心的型号变化。
希望这有所帮助,请让我知道是否有任何混淆或不够详细。
我不确定这是否直接回答你的问题,但我认为这是相关的:
http://lostechies.com/derickbailey/2011/10/11/backbone-js-getting-the-model-for-a-clicked-element/
当然,我设立这篇文章的背景是不同的,但我认为我提供的两种解决方案,以及每种解决方案的优缺点,都应该让您朝着正确的方向前进。
对我来说,似乎并不是世界上最糟糕的想法,通过某种标志来区分初始设置和后续的视图设置。 为了使这个干净和容易,该标志应该被添加到你自己的视图中,这应该扩展主干(基础)视图。
与Derick相同我不完全确定这是否直接回答您的问题,但我认为在这方面至少值得一提。
另请参阅:在骨干中使用Eventbus
链接地址: http://www.djcxy.com/p/35355.html上一篇: How to handle initializing and rendering subviews in Backbone.js?
