使用Marionette最大限度地减少回流焊

时间:2015-01-13 15:12:34

标签: javascript marionette

我有一个使用Marionette的Web应用程序,经常需要渲染大型嵌套视图树。我希望通过确保在父容器放入DOM之前呈现子元素并将其放置在父容器中来避免尽可能多的重排。如何渲染这些子元素以实现此目的?

我目前正在onRender函数中呈现所有子视图,据我所知,它们与父级同时显示,但我无法找到任何视图文档告诉我这是它的预期用途。我也考虑过onBeforeShow的使用,我知道在将视图放入DOM之前就已经调用了它,但它使重新渲染变得更加复杂,因为我不能只调用{ {1}}然后。

根据要求,这里有一些代码可以更好地了解我的情况:

render

如果我选择使用var Layout = LayoutView.extend({ //... initialize: function() { listenTo(this.model, "some event indicating that data changed", this.render); }, onRender: function() { //Will this new view render at the appropriate time to minimize page reflows? this.someRegion.show(new view(new model())); } }); ,那么我可以将引用传递给容器区域

onBeforeShow

或者让父对象监听并告诉视图何时渲染

var Layout = LayoutView.extend({
    //...

    initialize: function() {
        listenTo(this.model, "some event indicating that data changed", this.callback);
    },

    callback: function() {
        this.container.show(this, {forceShow: true});
    },

    onBeforeShow: function() {
        //Does not cause a page reflow, but this solution isn't pretty.
        this.someRegion.show(new View(new Model()));
    }
});

var LayoutContainer = LayoutView.extend({
    //...

    onRender: function() {
        var model = new LayoutModel();
        var layout = new Layout({
            model: model,
            container: this.containerRegion
        });
        this.containerRegion.show(layout);
    }
});

3 个答案:

答案 0 :(得分:2)

在您需要采取行动的级别聆听您的模型

当视图el已经附加到DOM时,渲染会立即将视图附加到DOM,因此顶级视图将立即附加到DOM。这意味着如果你在onRender中有你的回调,它们将按顺序连接,你将有回流。

如果您将子视图显示放入onBeforeShow,则会构建一个将显示一次的视图树,假设顶部视图已调用show。所以问题是在哪里调用顶级show。我看到2个选项

简单选项

最简单的方法是让您的顶级LayoutView在onRender中显示其子项,然后所有后续子视图都会在onBeforeShow中显示其子项。这可能会导致一些油漆(顶层,然后是它所显示的每个区域的一个),但不是它们的巨大级联,并且可能不是性能问题(当然听起来你没有可见的性能问题。)

稍微复杂的选项

如果你真的只需要对模型更改进行一次重新绘制,那么你应该按照问题中的描述创建一个包装视图,但不要让子视图直接引用它,确保包装View有对模型的引用,并在模型更改时重新显示您的布局。然后,您的所有回调都可以是onBeforeShow,并且更改将导致一次绘制。视图之间没有连接,所有内容都将在同一个回调中一致显示。

var LayoutContainer = LayoutView.extend({
    initialize: function() {
        this.listenTo(this.model,'change',this.reshowLayout);
    },

    reshowLayout: function() {
        var layout = new Layout({
            model: this.model,
            container: this.containerRegion
        });
        this.containerRegion.show(layout);
    }
});

答案 1 :(得分:1)

Marionette插入DOM last

Marionette实际上并没有将Collection / CompositeView的孩子附加到DOM,直到呈现所有(父节点和父节点下的子节点) 。看看这是如何发生的:

Region.show()

show: function () {
  // ...
  if (_shouldShowView) {
    view.render();
    // ...
    this.attachHtml(view);
  }      
  // ...
}

region.show()会致电view.render(),致电CompositeView.render()

render: function() {
  //...
  this._renderRoot();
  this._renderChildren();
  //...
  return this;
}

并在该函数中注意先调用view_renderRoot()然后调用view._renderChildren()。这些函数不会将渲染的视图附加到DOM。它们只是填充模板并在内存中加载HTML节点。

最后在DOM

回到Regions.show()region.attachHtml()实际上是将视图插入DOM:

attachHtml: function(view) {
  // empty the node and append new view
  this.el.innerHTML='';
  this.el.appendChild(view.el);
},

如果您没有使用区域经理,并且.render()您自己的观点,那么您仍然需要像region.attachHtml()那样将视图附加到DOM。

在插入DOM之前,所有子项都附加到父项

在CompositeView中,渲染的子项将附加到CompositeView.attachBuffer()中的父视图(如果它是集合的第一个渲染,或collection.reset())或CompositeView._insertBefore / {{1 (可排序/不可分类的孩子)。

所有这些最终都会实现某种形式的_insertAfter

在您的场景中,您提到父级尚未加入DOM,然后这些都附加到仅存在于内存中的collectionView.$el.append(childView.el)

忘记事件

Marionette用于渲染和注入儿童视图的方式的美妙之处在于,您可以忘记来渲染孩子。只要您从头开始正确设置嵌套种子,该过程将是递归的,并且在呈现最后一个子项之前,顶部视图将不会返回。这意味着在将节点附加到DOM之前,所有子节点都将附加到树上。

如果您对如何为嵌套视图设定种子有特定问题,请发布相关代码,我可以为您提供更多指示。

答案 2 :(得分:1)

覆盖LayoutView.render()

根据评论,在问题澄清后,我提出了这个解决方案:

LayoutView.render()执行一些内务处理,然后只需拨打ItemView.render()。我建议您覆盖父LayoutView.render()中的LayoutView,如下所示

render: function() {
  this._ensureViewIsIntact();

  if (this._firstRender) {
    // if this is the first render, don't do anything to
    // reset the regions
    this._firstRender = false;
  } else {
    // If this is not the first render call, then we need to
    // re-initialize the `el` for each region
    this._reInitializeRegions();
  }

  var layout = Marionette.ItemView.prototype.render.apply(this, arguments);

  // After the parent rendered render and attach the child
  this.someRegion.show(new view(new model()));

  return layout;

},

我还没有对此进行测试(过去没有尝试过),但它足够疯狂以至于它可能会起作用。