如何处理主干.js中的初始化和呈现子视图?

2022-08-30 01:24:59

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


方案一:

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

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);

  • 通过追加子级,可以强制呈现函数了解父级 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,则将创建一个全新的子视图,并且其所有当前状态都将丢失。这似乎也会因为内存泄漏而变得很糟糕。


真的很好奇让你们的家伙对此的看法。您将使用哪种方案?还是有第四个神奇的解决所有这些问题?

您是否曾经跟踪过视图的呈现状态?说一面旗帜?似乎真的很卡顿。renderedBefore


答案 1

这是一个很好的问题。Backbone很棒,因为它缺乏假设,但这确实意味着你必须(决定如何)自己实现这样的事情。在浏览了我自己的东西之后,我发现我(有点)混合使用了场景1和场景2。我不认为存在第4个神奇的场景,因为很简单,你在场景1和2中所做的一切都必须完成。

我认为用一个例子来解释我喜欢如何处理它是最简单的。假设我把这个简单的页面分解成指定的视图:

Page Breakdown

假设 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如何与图表匹配是非常明显的。

包含 2 个子视图,以及一些额外的 div,其中一个 ,需要在某个时候设置。 保存自己的子视图,即条目数组。ParentViewInfoViewPhoneListView#namePhoneListViewPhoneView

所以继续你的实际问题。我根据视图类型以不同的方式处理初始化和呈现。我将我的观点分为两种类型,视图和视图。ParentChild

它们之间的区别很简单,视图包含子视图,而视图则不具有子视图。因此,在我的示例中,并且是视图,而和条目是视图。ParentChildParentViewPhoneListViewParentInfoViewPhoneViewChild

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

更详细地说,对于视图,我喜欢我的函数做一些事情:Parentinitialize

  1. 初始化我自己的视图
  2. 渲染我自己的视图
  3. 创建并初始化任何子视图。
  4. 在我的视图中为每个子视图分配一个元素(例如,将分配 )。InfoView#info

步骤 1 非常不言自明。

步骤 2(呈现)的完成是为了让子视图所依赖的任何元素在我尝试分配它们之前已经存在。通过这样做,我知道所有的孩子都将被正确设置,我可以根据需要多次重新渲染他们的块,而不必担心必须重新委派任何内容。我实际上没有任何孩子在这里的观点,我允许他们在自己的范围内这样做。eventsrenderinitialization

步骤 3 和 4 实际上与我在创建子视图时传入的时间同时处理。我喜欢在这里传递一个元素,因为我觉得父母应该确定在自己的视图中允许孩子放置其内容的位置。el

对于渲染,我试图让它对视图保持非常简单。我希望该函数只做呈现父视图。没有事件委派,没有子视图的呈现,什么都没有。只是一个简单的渲染。Parentrender

有时这并不总是有效。例如,在我上面的示例中,每当模型中的名称更改时,都需要更新元素。但是,此块是模板的一部分,不是由专用视图处理的,因此我解决此问题。我将创建某种函数,该函数仅替换元素的内容,而不必丢弃整个元素。这可能看起来像是一个黑客,但我真的发现它比担心重新渲染整个DOM和重新附加元素等更好。如果我真的想让它变得干净,我会创建一个新的视图(类似于)来处理块。#nameParentViewChildsubRender#name#parentChildInfoView#name

现在对于视图,它与视图非常相似,只是没有创建任何进一步的视图。所以:ChildinitializationParentChild

  1. 初始化我的视图
  2. 安装程序绑定侦听我关心的模型的任何更改
  3. 渲染我的视图

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);
    }
});

你可以在这里看到我的实现。通过将更改绑定到 而不是 ,我不必担心爆炸和重建整个块。subRendersubRenderrender

下面是该块的示例代码: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

将类似于 ,您只需要在 和 函数中都多一点逻辑来处理集合。如何处理集合实际上取决于您,但您至少需要侦听集合事件并决定如何呈现(追加/删除,或者只是重新呈现整个块)。我个人喜欢附加新视图并删除旧视图,而不是重新渲染整个视图。PhoneListViewParentViewinitializationrender

将几乎与 相同,只听它所关心的模型变化。PhoneViewInfoView

希望这有所帮助,如果有任何令人困惑或不够详细,请告诉我。


答案 2

我不确定这是否直接回答了你的问题,但我认为它是相关的:

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

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