【问题标题】:Angular 2 rebuilds DOM in ngFor even when trackBy used即使使用了 trackBy,Angular 2 也会在 ngFor 中重建 DOM
【发布时间】:2017-04-19 04:12:12
【问题描述】:

我非常不需要为一组值重建 DOM,每个值都包含一个表示 YouTube 直播的对象。我正在使用 ngFor 来迭代直播。

但是,当直播发生变化时(这可能是因为用户想要改变观看顺序,所以他们最想观看的直播在顶部 - 参见下面的reorder()),ngFor 会重建元素,这会导致 YouTube 嵌入从 DOM 中移除并重新插入;这会导致正在运行的直播停止播放,并重新加载它们 - 这需要一秒钟左右的时间。这不是最佳选择。

Here is a Plunkr of my issue

我尝试使用trackBy 来跟随直播名称(我的对象中的一个属性)而不是对象本身的值,但这似乎没有解决问题。

模板:

<livestream-player *ngFor="let livestream of userPrefs.visibleLivestreams(); trackBy: livestreamTrackByFn"        [video]="livestream.url | sanitize:'resource'">
</livestream-player>

元素数组:userPrefs.visibleLivestreams()

public visibleLivestreams() : Livestream[] {

   let livestreams = [
       { name: 'a', url: 'youtubeUrl', ... }, 
       { name: 'b', url: 'youtubeUrl', ... },
       { name: 'c', url: 'youtubeUrl', ... }
   ];

   // _visibleLivestreams, used below to order and filter, 
   // is a simple, ordered, array of strings representing 
   // the livestream names, stored in localstorage.

   return livestreams
        .filter(l => this._visibleLivestreams.indexOf(l.name) != -1);
        .sort((a, b) => this._visibleLivestreams.indexOf(a.name) < this._visibleLivestreams.indexOf(b.name) ? -1 : 1);
}

*ngFor 模板中使用的 trackBy 功能无效

public livestreamTrackByFn(index: number, item: Livestream) : string {
    return item.name;
}

重新排序功能

/**
 * Reorders elements of the livestreams array by shifting off the
 * 0th element, and pushing it the last position in the array.
 * 
 * Calling this function results in the DOM being rebuilt!
 */
public rotate() : void {
    // shift element off _visibleLivestreams
    let elem = this.userPrefs.visibleLivestreams.shift();
    // push element onto _visibleStreams
    this.userPrefs.visibleLivestreams.push(elem);
}

由于上述实现不必要地重建 DOM,我真的在寻找解决这个问题的方法。到目前为止,我的想法是创建一个自定义的*ngFor-like 结构指令,使用 flexbox 来管理订单,或者只是在我现有的实现中确定一个小的更改来解决这个问题。

【问题讨论】:

标签: javascript angular ngfor


【解决方案1】:

这是因为public visibleLivestreams() : Livestream[] { 每次调用时都会返回一个新数组。 trackBy 帮不上忙。

而是将结果分配给一个属性并绑定到该属性。

constructor() { // or some other event that causes the result to change
    this.liveStreams = this.userPrefs.visibleLivestreams();
<livestream-player *ngFor="let livestream of userPrefs.visibleLivestreams"        [video]="livestream.url | sanitize:'resource'">

这样效率更高,因为每次更改检测运行时,您的代码更改检测都会调用 userPrefs.visibleLivestreams();,而且这种情况可能非常频繁。

【讨论】:

  • 好的,但是当直播确实发生变化时,我需要以不同的顺序重新显示它们,我仍然需要重新分配this.liveStreams,这将导致元素无论如何删除并重新添加,对吗?
  • 当然可以。只需重用以前的数组并根据需要删除和添加元素,而不是创建新数组。
  • 我现在.sort正在更改数组(一个改变原数组的可变函数),*ngFor 仍在删除和添加元素。想法?
  • 你能在 Plunker 中复制吗?
  • 当然!单击“旋转”以查看直播重新加载(奇怪的是底部除外)embed.plnkr.co/sMSwU2wLFvKpBNgmNTJK 这是我需要防止的重新加载效果,同时保持重新排序的能力。
【解决方案2】:

当页面第一次加载时,ngFor 会加载整个列表。之后,如果您当时正在循环的列表中有更改,则只会反映更新的项目。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-19
    • 2018-09-27
    • 2017-03-04
    • 2019-11-19
    • 2018-07-03
    • 1970-01-01
    • 2017-06-25
    • 2020-10-25
    相关资源
    最近更新 更多