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