如何处理Backbone.js中的初始化和渲染子视图?

2020/10/17 11:21 · javascript ·  · 0评论

我有三种不同的方法来初始化和渲染视图及其子视图,每种方法都有不同的问题。我很好奇,是否有更好的方法可以解决所有问题:


方案一:

在父级的初始化函数中初始化子级。这样,并不是所有内容都卡在渲染中,从而减少了对渲染的阻塞。

initialize : function () {

    //parent init stuff

    this.child = new Child();
},

render : function () {

    this.$el.html(this.template());

    this.child.render().appendTo(this.$('.container-placeholder');
}

问题:

  • 最大的问题是第二次在父级上调用render将删除所有childs事件绑定。(这是因为jQuery的$.html()工作原理。)这可以通过调用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();
}

问题:

  • 这意味着现在还必须将渲染功能与所有初始化逻辑联系在一起。

  • 如果我编辑其中一个子视图的状态,然后在父级上调用render,则将创建一个全新的子级,并且其所有当前状态都将丢失。这似乎也可以解决内存泄漏问题。


真的很想让你们接受这一点。您将使用哪种方案?还是有第四个神奇的魔法可以解决所有这些问题?

您是否曾经跟踪过View的渲染状态?说一个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拥有2个视图,InfoView并且PhoneListView还有一些额外的div,其中之一,#name,需要在某些点进行设置。 PhoneListView拥有自己的子视图(一组PhoneView条目)。

接下来是您的实际问题。我根据视图类型以不同方式处理初始化和渲染。我将视图分为两种类型:Parent视图和Child视图。

它们之间的区别很简单,Parent视图持有子视图,而Child视图不持有子视图因此,在我的示例中,ParentViewPhoneListViewParent视图,而InfoView和则PhoneViewChild视图。

就像我之前提到的,这两个类别之间的最大区别是允许它们渲染的时间。在理想的世界中,我希望Parent视图只能渲染一次。当模型更改时,由其子视图来处理任何重新渲染。Child另一方面,我允许在需要时随时重新渲染,因为它们没有其他依赖于它们的视图。

更详细一点,对于Parent视图,我希望我的initialize函数可以做一些事情:

  1. 初始化我自己的观点
  2. 呈现我自己的观点
  3. 创建并初始化所有子视图。
  4. 在我的视图中为每个子视图分配一个元素(例如InfoView将分配#info)。

第1步很容易解释。

完成第2步的渲染,以便子视图依赖的任何元素在我尝试分配它们之前都已经存在。通过这样做,我知道所有孩子events都将正确设置,并且我可以根据需要多次渲染它们的块,而不必担心必须重新委托任何东西。render在这里实际上没有任何孩子的观点,我允许他们自己做initialization

实际上el,在创建子视图时,第3步和第4步实际上是在我传入的同时进行的。我喜欢在此处传递一个元素,因为我认为父母应该确定允许孩子放置其内容的位置。

对于渲染,我尝试使其对于Parent视图非常简单我希望render函数只渲染父视图。没有事件委托,没有呈现子视图,什么也没有。只是一个简单的渲染。

有时候,这并不总是有效。例如,在上面的示例中,#name只要模型中的名称发生更改就需要更新元素。但是,此块是ParentView模板的一部分,而不是由专用Child视图处理的,因此我要解决此问题。我将创建某种subRender函数,函数替换#name元素的内容,而不必浪费整个#parent元素。这似乎是一种hack,但是我真的发现它比担心重新渲染整个DOM和重新附加元素等要好。如果我真的想使其干净,则可以创建一个新的Child视图(类似于InfoView)来处理该#name块。

现在,对于Child视图,该视图initializationParent视图非常相似,只是没有创建任何其他Child视图。所以:

  1. 初始化我的观点
  2. 安装程序绑定侦听我关心的模型的任何更改
  3. 呈现我的观点

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类似于,ParentViewinitializationrender函数只需要更多的逻辑来处理集合。处理集合的方式实际上取决于您,但是您至少需要侦听集合事件并确定要渲染的方式(追加/删除,或仅重新渲染整个块)。我个人喜欢添加新视图并删除旧视图,而不是重新渲染整个视图。

PhoneView将与几乎相同InfoView,仅听模型关心的更改。

希望这对您有所帮助,请让我知道是否有任何令人困惑或不够详细的内容。

我不确定这是否可以直接回答您的问题,但我认为这很重要:

http://lostechies.com/derickbailey/2011/10/11/backbone-js-getting-the-model-for-a-clicked-element/

当然,我设置本文的背景是不同的,但是我认为我提供的两种解决方案以及每种解决方案的优缺点都应该使您朝着正确的方向前进。

在我看来,通过某种标记来区分视图的初始设置和后续设置似乎不是世界上最糟糕的想法。为了使此操作变得简单明了,应将标志添加到您自己的视图中,该视图应扩展主干(基本)视图。

与Derick一样,我不确定这是否可以直接回答您的问题,但我认为至少在这种情况下值得一提。

另请参阅:在骨干网中使用Eventbus

凯文·皮尔(Kevin Peel)给出了一个很好的答案-这是我的tl; dr版本:

initialize : function () {

    //parent init stuff

    this.render(); //ANSWER: RENDER THE PARENT BEFORE INITIALIZING THE CHILD!!

    this.child = new Child();
},

我试图避免像这样的观点之间的耦合。我通常有两种方法:

使用路由器

基本上,让路由器功能初始化父视图和子视图。因此,视图彼此之间并不了解,但是路由器会处理所有这些。

将相同的el传递给两个视图

this.parent = new Parent({el: $('.container-placeholder')});
this.child = new Child({el: $('.container-placeholder')});

两者都拥有相同的DOM知识,您可以随时订购它们。

我要做的是给每个孩子一个身份(Backbone已经为您完成了此操作:cid)

当Container进行渲染时,使用'cid'和'tagName'为每个子代生成一个占位符,因此在模板中,子代不知道容器将其放置在何处。

<tagName id='cid'></tagName>

比你可以使用的

Container.render()
Child.render();
this.$('#'+cid).replaceWith(child.$el);
// the rapalceWith in jquery will detach the element 
// from the dom first, so we need re-delegateEvents here
child.delegateEvents();

不需要指定的占位符,并且Container仅生成占位符,而不生成子代的DOM结构。Cotainer和Children仍仅生成一次自己的DOM元素。

这是用于创建和呈现子视图的轻量级混合,我认为可以解决此线程中的所有问题:

https://github.com/rotundasoftware/backbone.subviews

此插件采用的方法是在一次渲染父视图之后创建并渲染子视图然后,在父视图的后续渲染中,$。detach子视图元素,重新渲染父视图,然后将子视图元素插入适当的位置并重新渲染它们。这样,子视图对象可在后续渲染中重用,并且无需重新委托事件。

请注意,集合视图(集合中的每个模型都由一个子视图表示)的情况是完全不同的,我认为应该有自己的讨论/解决方案。对于这种情况,我知道最好的一般解决方案是Marionette中CollectionView

编辑:对于集合视图的情况,如果您需要基于单击和/或拖放以进行重新排序的模型选择,则您可能还想查看这个更加注重UI的实现

本文地址:http://javascript.askforanswer.com/ruhechulibackbone-jszhongdechushihuahexuanranzishitu.html
文章标签: ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!