Angular 2缓存可观察的http结果数据
Angular 2 cache observable http result data
我有一个通过 HTTP 服务和 returns 可观察对象获取数据的服务。
第一次调用后,我想在服务内部缓存结果,一旦新组件尝试获取数据,它将从缓存的结果中获取数据。
有没有简单的解决方案?
如果您将 observables 作为一种共享数据的方式,您可以采用以下方法:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, ReplaySubject } from 'rxjs';
import { SomeModel } from 'path/to/it';
@Injectable({
providedIn: 'root'
})
export class CachedService {
private dataSubject = new ReplaySubject<SomeModel>(1);
data$: Observable<SomeModel> = this.dataSubject.asObservable();
constructor(private http: HttpClient) { }
fetch() {
this.http.get<SomeModel>(...).subscribe(res => this.dataSubject.next(res));
}
}
这将在调用 fetch
方法时进行 HTTP 调用,service.data$
的任何订阅者都将从 ReplaySubject
获得响应。由于它会重播之前的值,任何在 HTTP 调用解决后 加入的订阅者仍将获得之前的响应。
如果要触发更新,只需调用 service.fetch()
启动新的 HTTP 调用,新响应到达后所有订阅者都会更新。
您的组件将类似于:
@Component({ ... })
export class SomeComponent implements OnDestroy, OnInit {
private subscription?: Subscription;
constructor(private service: CachedService) { }
ngOnInit() {
this.service.fetch();
this.subscription = this.service.data$.subscribe(...);
}
ngOnDestroy() {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
}
我最近为我的同事写了一篇关于这种方法的博客文章:http://blog.jonrshar.pe/2017/Apr/09/async-angular-data.html
我认为您不应该在构造函数中或 angular 生命周期中的任何时间执行 fetch()。正如您所说,ngOnInit
在 angular 服务中不起作用。
Instead we want to leverage rxjs to seamlessly pass us cached values through the stream – without the caller having to know anything about cached vs non cached values.
如果一个组件需要一个数据,它会订阅它,不管它是否是缓存。为什么要 fetch() 一个你不确定它会被使用的数据?
缓存应该在更高级别实现。我认为这种实施是一个好的开始:
http://www.syntaxsuccess.com/viewarticle/caching-with-rxjs-observables-in-angular-2.0
getFriends(){
if(!this._friends){
this._friends = this._http.get('./components/rxjs-caching/friends.json')
.map((res:Response) => res.json().friends)
.publishReplay(1)
.refCount();
}
return this._friends;
}
我不确定这是最好的方法,但它更容易维护,因为它具有单一的职责。只有当一个组件订阅它时,数据才会被缓存,无论 what/who/which 组件是否需要数据并且是第一个需要它的。
您可以构建简单的 class Cacheable<> 来帮助管理从 http 服务器或其他任何其他来源检索到的数据的缓存:
declare type GetDataHandler<T> = () => Observable<T>;
export class Cacheable<T> {
protected data: T;
protected subjectData: Subject<T>;
protected observableData: Observable<T>;
public getHandler: GetDataHandler<T>;
constructor() {
this.subjectData = new ReplaySubject(1);
this.observableData = this.subjectData.asObservable();
}
public getData(): Observable<T> {
if (!this.getHandler) {
throw new Error("getHandler is not defined");
}
if (!this.data) {
this.getHandler().map((r: T) => {
this.data = r;
return r;
}).subscribe(
result => this.subjectData.next(result),
err => this.subjectData.error(err)
);
}
return this.observableData;
}
public resetCache(): void {
this.data = null;
}
public refresh(): void {
this.resetCache();
this.getData();
}
}
用法
声明可缓存<>对象(大概是服务的一部分):
list: Cacheable<string> = new Cacheable<string>();
和处理程序:
this.list.getHandler = () => {
// get data from server
return this.http.get(url)
.map((r: Response) => r.json() as string[]);
}
来自组件的调用:
//gets data from server
List.getData().subscribe(…)
这里有更多详细信息和代码示例:http://devinstance.net/articles/20171021/rxjs-cacheable
我有一个通过 HTTP 服务和 returns 可观察对象获取数据的服务。
第一次调用后,我想在服务内部缓存结果,一旦新组件尝试获取数据,它将从缓存的结果中获取数据。
有没有简单的解决方案?
如果您将 observables 作为一种共享数据的方式,您可以采用以下方法:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, ReplaySubject } from 'rxjs';
import { SomeModel } from 'path/to/it';
@Injectable({
providedIn: 'root'
})
export class CachedService {
private dataSubject = new ReplaySubject<SomeModel>(1);
data$: Observable<SomeModel> = this.dataSubject.asObservable();
constructor(private http: HttpClient) { }
fetch() {
this.http.get<SomeModel>(...).subscribe(res => this.dataSubject.next(res));
}
}
这将在调用 fetch
方法时进行 HTTP 调用,service.data$
的任何订阅者都将从 ReplaySubject
获得响应。由于它会重播之前的值,任何在 HTTP 调用解决后 加入的订阅者仍将获得之前的响应。
如果要触发更新,只需调用 service.fetch()
启动新的 HTTP 调用,新响应到达后所有订阅者都会更新。
您的组件将类似于:
@Component({ ... })
export class SomeComponent implements OnDestroy, OnInit {
private subscription?: Subscription;
constructor(private service: CachedService) { }
ngOnInit() {
this.service.fetch();
this.subscription = this.service.data$.subscribe(...);
}
ngOnDestroy() {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
}
我最近为我的同事写了一篇关于这种方法的博客文章:http://blog.jonrshar.pe/2017/Apr/09/async-angular-data.html
我认为您不应该在构造函数中或 angular 生命周期中的任何时间执行 fetch()。正如您所说,ngOnInit
在 angular 服务中不起作用。
Instead we want to leverage rxjs to seamlessly pass us cached values through the stream – without the caller having to know anything about cached vs non cached values.
如果一个组件需要一个数据,它会订阅它,不管它是否是缓存。为什么要 fetch() 一个你不确定它会被使用的数据?
缓存应该在更高级别实现。我认为这种实施是一个好的开始: http://www.syntaxsuccess.com/viewarticle/caching-with-rxjs-observables-in-angular-2.0
getFriends(){
if(!this._friends){
this._friends = this._http.get('./components/rxjs-caching/friends.json')
.map((res:Response) => res.json().friends)
.publishReplay(1)
.refCount();
}
return this._friends;
}
我不确定这是最好的方法,但它更容易维护,因为它具有单一的职责。只有当一个组件订阅它时,数据才会被缓存,无论 what/who/which 组件是否需要数据并且是第一个需要它的。
您可以构建简单的 class Cacheable<> 来帮助管理从 http 服务器或其他任何其他来源检索到的数据的缓存:
declare type GetDataHandler<T> = () => Observable<T>;
export class Cacheable<T> {
protected data: T;
protected subjectData: Subject<T>;
protected observableData: Observable<T>;
public getHandler: GetDataHandler<T>;
constructor() {
this.subjectData = new ReplaySubject(1);
this.observableData = this.subjectData.asObservable();
}
public getData(): Observable<T> {
if (!this.getHandler) {
throw new Error("getHandler is not defined");
}
if (!this.data) {
this.getHandler().map((r: T) => {
this.data = r;
return r;
}).subscribe(
result => this.subjectData.next(result),
err => this.subjectData.error(err)
);
}
return this.observableData;
}
public resetCache(): void {
this.data = null;
}
public refresh(): void {
this.resetCache();
this.getData();
}
}
用法
声明可缓存<>对象(大概是服务的一部分):
list: Cacheable<string> = new Cacheable<string>();
和处理程序:
this.list.getHandler = () => {
// get data from server
return this.http.get(url)
.map((r: Response) => r.json() as string[]);
}
来自组件的调用:
//gets data from server
List.getData().subscribe(…)
这里有更多详细信息和代码示例:http://devinstance.net/articles/20171021/rxjs-cacheable