Firebase Angular2 - 如何在没有太多开销的情况下查询和呈现列表

Firebase Angular2 - how to query and render lists without too much overhead

在我的应用程序中,我有以下 firebase posts 结构:

posts: {
    text: ".."
    meta: {..}
    user: {
        id: "user1",
        username: ".."
    }
}

我正在使用 angularfire2。 因此,要获得特定用户的所有 posts posted,我 运行 以下查询:

this.userPosts$ = this.af.database.list('/posts', {
    query: {
      orderByChild: 'user/id',
      equalTo: userId
    }
  }).map( (posts) => {
    return posts.map(post => Post.unpack(post));
  }).publishReplay(1).refCount();

我听说它不是很有效,因为每次发生变化时(即使在单个 post 中),整个列表都会重新加载,因此会重新呈现。由于我在我的应用程序中有喜欢和不喜欢的东西,所以这种情况会经常发生。基本上每次有人喜欢 post,整个列表都会为每个用户重新加载,如果我没记错的话。

我知道有一种方法可以在监听 firebase 事件时维护列表的本地副本:"child_added"、"child_removed" 和 "child_changed"。 但是,如果我将这些事件附加到查询列表中,它们将在每次原始 posts 列表作为一个整体发生变化时触发。

所以,我正在考虑在这里采取什么方法。如果这是唯一好的选择,我可能会将 posts 数据复制到 firebase 中的 "user_posts" 列表中。但是,通过这种方法,我觉得查询几乎变得毫无用处,而且我失去了灵活性。大概一周前才开始用firebase,所以可能还有一些误会。

在研究这个问题时,我发现 an issueFirebaseListObservable 实现一起使用,而 preserveSnapshotfalse - 默认行为。每次可观察对象发出一组未包装的快照时,数组元素都是不同的实例——即使元素本身没有改变。这导致变更检测的效率大大降低。

一个fix has been merged and will be included in the next release of AngularFire2 (that is, the release that follows 2.0.0-beta.7) - which should be soon.

修复后,如果使用 Angular 的 OnPush 变化检测,AngularFire2 的 FirebaseListObservable 在 DOM 更新方面非常快速和高效.

我通常将组件拆分为 container and presentational components,容器组件包含可观察对象,展示组件使用 OnPush 变化检测。

容器组件看起来像这样:

import { Component } from "@angular/core";
import { AngularFire, FirebaseListObservable } from "angularfire2";

@Component({
  selector: "items-container",
  template: `<items-component [items]="items | async"></items-component>`
})
export class ItemsContainer {

  private items: FirebaseListObservable<any>;

  constructor(private angularFire: AngularFire) {
    this.items = angularFire.database.list("some/fast-changing/data", {
      query: {
        limitToLast: 1000
      }
    });
  }
}

演示组件看起来像这样:

import { ChangeDetectionStrategy, Component, Input } from "@angular/core";

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: "items-component",
  template: `
    <ul>
      <li *ngFor="let item of items">{{ item | json }}</li>
    </ul>
  `
})
export class ItemsComponent {
  @Input() items: any[];
}

通过这种组件安排,Angular 的 OnPush 更改检测将确保只有实际更改的元素才会影响 DOM 更新。因此,即使很长,滚动列表也会高效且快速。

另一个提示:使用限制查询的 FirebaseListObserable 有时会在基础数据更改时发出两个值。例如,当一个新项目被添加到数据库时,查询最近 10 个项目的列表(使用 limitToLast: 10)将发出一个数组,其中删除了最近的项目,然后将立即发出另一个具有最近的项目添加的项目。如果您不想发出这两个数组中的第一个,可以使用 auditTime 运算符:

import "rxjs/add/operator/auditTime";

this.items = angularFire.database.list("some/fast-changing/data", {
  query: {
    limitToLast: 10
  }
})
.auditTime(0);