Angular 9中用RxJS Observable实现延迟显示骨架屏
优雅实现:仅当请求超500ms时显示骨架屏(Angular 9 + RxJS)
你的思路完全正确!核心就是延迟触发loading状态,避免快速请求时的骨架屏闪烁,用RxJS的Observable可以非常优雅地实现这个逻辑,而且完全贴合Angular的响应式编程风格。
实现方案思路
我们可以通过两个Observable的组合来实现:
- 一个是你的REST请求流,负责获取数据
- 另一个是500ms的定时器流,负责延迟触发loading状态
利用RxJS的race操作符,让这两个流“竞争”:如果请求在500ms内完成,就直接跳过loading;如果500ms到了请求还没结束,就触发loading显示骨架屏。同时用async管道自动管理订阅,避免手动操作带来的内存泄漏问题。
具体代码实现
组件逻辑(.ts文件)
import { Component, OnInit } from '@angular/core'; import { Observable, timer, of, race } from 'rxjs'; import { map, shareReplay, catchError, concatWith, startWith } from 'rxjs/operators'; import { YourDataService } from './your-data.service'; // 替换成你的数据服务 @Component({ selector: 'app-item-list', templateUrl: './item-list.component.html' }) export class ItemListComponent implements OnInit { items$: Observable<any[]>; loading$: Observable<boolean>; constructor(private dataService: YourDataService) { } ngOnInit(): void { // 定义请求流,用shareReplay确保只发一次请求 const dataRequest$ = this.dataService.fetchItems().pipe( shareReplay(1), // 处理请求错误,确保loading能正常关闭 catchError((error) => { console.error('数据加载失败:', error); return of([]); }) ); // 构建loading状态流: // 1. 500ms后发出true(准备显示骨架屏) // 2. 如果请求先完成,就发出false(跳过骨架屏) // 3. 初始状态为false,请求完成后最终状态也为false this.loading$ = race( timer(500).pipe(map(() => true)), dataRequest$.pipe(map(() => false)) ).pipe( startWith(false), concatWith(of(false)) ); // 赋值数据流供模板使用 this.items$ = dataRequest$; } }
模板代码(.html文件)
<div *ngIf="!(loading$ | async); else skeleton"> <span *ngFor="let item of items$ | async">{{item}}</span> </div> <ng-template #skeleton> <my-skeleton></my-skeleton> </ng-template>
关键细节说明
race操作符:它会订阅所有传入的Observable,第一个发出值的Observable会被采纳,其他Observable会被自动取消订阅。完美实现“谁快听谁的”逻辑。shareReplay(1):确保dataRequest$被多个订阅者(loading$和items$)共享,不会触发多次REST请求。async管道:自动管理Observable的订阅和取消,避免内存泄漏,同时让模板代码更简洁。- 错误处理:通过
catchError确保即使请求失败,loading状态也能正确关闭,不会一直显示骨架屏。
这种方案完全满足你的需求,既解决了骨架屏闪烁的问题,又符合Angular的最佳实践,代码简洁易维护。
内容的提问来源于stack exchange,提问作者Slash90ITA
相关产品推荐
相关产品推荐

