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 issue 与 FirebaseListObservable
实现一起使用,而 preserveSnapshot
是 false
- 默认行为。每次可观察对象发出一组未包装的快照时,数组元素都是不同的实例——即使元素本身没有改变。这导致变更检测的效率大大降低。
一个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);
在我的应用程序中,我有以下 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 issue 与 FirebaseListObservable
实现一起使用,而 preserveSnapshot
是 false
- 默认行为。每次可观察对象发出一组未包装的快照时,数组元素都是不同的实例——即使元素本身没有改变。这导致变更检测的效率大大降低。
一个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);