如何处理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?