在我的应用程序中,我有以下firebase帖子结构:
posts: { text: ".." meta: {..} user: { id: "user1", username: ".." } }
我正在使用angularfire2.因此,要获取特定用户发布的所有帖子,我运行以下查询:
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();
我听说它效率不高,因为每次有变化(即使在一个帖子中),整个列表都会被重新加载,从而重新渲染.由于我在我的应用程序中有喜欢和不喜欢,这将经常发生.基本上每当有人喜欢帖子时,如果我没有弄错的话,每个用户都会重新加载整个列表.
我知道有一种方法可以在监听firebase事件时维护列表的本地副本:"child_added","child_removed"和"child_changed".但是,如果我将这些事件附加到查询列表中,则每次原始帖子列表中的某些内容发生更改时,它们都会触发.
所以,我在想这里采取什么方法.我可能会将帖子数据复制到firebase中的"user_posts"列表中,如果它是唯一的好选择.然而,通过这种方法,我觉得查询变得几乎无用,我失去了灵活性.大约一个星期前我开始使用firebase,所以我可能仍会有一些误解.
当寻找到这个问题,我发现了一个问题与FirebaseListObservable
实现时preserveSnapshot
是false
-默认行为.每次observable发出一个未包装的快照数组时,数组元素都是不同的实例 - 即使元素本身没有改变.这导致变化检测效率低得多.
一个修补程序已被合并,并会被列入AngularFire2的下一个版本(也就是后面的版本2.0.0-beta.7
) -它应该很快.
通过修复,如果使用Angular的OnPush
变化检测,AngularFire2 FirebaseListObservable
在DOM更新方面非常快速有效.
我通常将组件分成容器和表示组件,容器组件使用OnPush
变化检测来保持可观察和表示组件.
容器组件看起来像这样:
import { Component } from "@angular/core";
import { AngularFire, FirebaseListObservable } from "angularfire2";
@Component({
selector: "items-container",
template: ` `
})
export class ItemsContainer {
private items: FirebaseListObservable;
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: `
- {{ item | json }}
`
})
export class ItemsComponent {
@Input() items: any[];
}
通过这种组件排列,Angular的OnPush
更改检测将确保只有实际更改的元素才会影响DOM更新.因此,即使很长时间,滚动列表也会高效而快速.
另一个提示:FirebaseListObserable
当底层数据发生变化时,使用限制查询的a有时会发出两个值.例如,当一个新项目添加到数据库时,查询最近10个项目(使用limitToLast: 10
)的列表将发出一个删除了最近项目的数组,然后立即发出另一个项目并添加最新项目.如果您不希望发出这两个数组中的第一个,则可以使用auditTime
运算符:
import "rxjs/add/operator/auditTime";
this.items = angularFire.database.list("some/fast-changing/data", {
query: {
limitToLast: 10
}
})
.auditTime(0);