性能优化:使用 shareReplay 避免重复工作
在 RxJS 中,一个常见的性能陷阱是多次订阅一个"冷"的 Observable,导致其背后的工作(如 HTTP 请求)被重复执行。shareReplay 是解决这个问题的关键操作符。
问题:重复的 HTTP 请求
想象一下,你有一个获取数据的 Observable,应用中的多个部分都需要订阅它。
typescript
import { ajax } from 'rxjs/ajax';
import { map } from 'rxjs/operators';
const dataJSON('/api/data$ = ajax.get').pipe(
map(response => {
console.log('--- HTTP Request Sent ---');
return response;
})
);
// Component A 订阅
data$.subscribe(data => console.log('Component A:', data));
// Component B 订阅
data$.subscribe(data => console.log('Component B:', data));输出:
--- HTTP Request Sent ---
Component A: { ...data }
--- HTTP Request Sent ---
Component B: { ...data }HTTP 请求被发送了两次!这是因为 ajax 创建的是一个冷的 Observable,每次订阅都会触发一次新的执行。
解决方案:shareReplay
shareReplay 操作符可以将一个冷的 Observable 转换为一个热的、可共享的 Observable。
typescript
import { ajax } from 'rxjs/ajax';
import { map, shareReplay } from 'rxjs/operators';
const data$ = ajax.getJSON('/api/data').pipe(
map(response => {
console.log('--- HTTP Request Sent ---');
return response;
}),
shareReplay(1)
);现在 HTTP 请求只会被发送一次,后续订阅者会从缓存中获取数据。
shareReplay 的参数和陷阱
shareReplay({ bufferSize: 1, refCount: true })
bufferSize: 决定缓存多少个值。对于 HTTP 请求,通常设置为1。refCount: 当订阅者数量变为 0 时,是否取消对源 Observable 的订阅。
最佳实践
对于只会发出一个值然后完成的流(如 HTTP 请求),使用 shareReplay(1) 是安全的。
对于长时活动的流,使用 shareReplay({ bufferSize: 1, refCount: true })。

