這里給大家分享我在網(wǎng)上總結(jié)出來的一些知識(shí),希望對(duì)大家有所幫助
在切換詳情頁中有這么一個(gè)場景,點(diǎn)擊上一條,會(huì)顯示上一條的詳情頁,同理,點(diǎn)擊下一條,會(huì)顯示下一條的詳情頁。
偽代碼如下所示:
我們定義了一個(gè)?switcher
?模版, 用戶點(diǎn)擊上一條、下一條時(shí)調(diào)用?goToPreOrNext
?方法。該頁面通過?loadingDone
?狀態(tài)判斷是否展示加載效果。
// html <thy-loading [thyDone]="loadingDone"></thy-loading> <ng-container *ngIf="loadingDone"> <styx-pivot-detail> ... <thy-arrow-switcher ... (thyPrevious)="goToPreOrNext($event)" (thyNext)="goToPreOrNext($event)" ></thy-arrow-switcher> ... </styx-pivot-detail> </ng-container>
在?goToPreOrNext
?方法中,當(dāng)調(diào)用該方法時(shí),通過?goToPreOrNextResolve
?接口返回下一條的詳情?id
,通過該?id
?請(qǐng)求詳情數(shù)據(jù)。
// 請(qǐng)求下一條 id fetchPreOrNext(event: ThyArrowSwitcherEvent) { ... this.goToPreOrNextResolve(event.index, topicId).subscribe( (id: string) => { this.getDetail(id); } ... ) } // 請(qǐng)求詳情數(shù)據(jù) getDetail(postId: string) { this.loadingDone = false; this.store .fetchPost(postId) .pipe( finalize(() => { this.loadingDone = true; }) ) .subscribe(); }
這看起來好像沒有什么問題,應(yīng)該一般都是這么干的,我們運(yùn)行看看。
???如何切換時(shí)候不閃?
與最上面的相比,有沒有發(fā)現(xiàn)每次切換時(shí),都會(huì)閃一下,用戶體驗(yàn)很不好,有沒有辦法可以解決它?
這個(gè)問題就是?loadingDone
?的狀態(tài)切換導(dǎo)致的,我們把?loadingDone
?干掉是不是就可以了?
如下代碼所示:
// 請(qǐng)求詳情數(shù)據(jù) getDetail(postId: string) { // 注釋掉這一行 // this.loadingDone = false; this.store .fetchPost(postId) .pipe( finalize(() => { this.loadingDone = true; }) ) .subscribe(); }
好像方案可行?
但是把網(wǎng)絡(luò)調(diào)成低速 3G 后,會(huì)發(fā)現(xiàn),我們的加載效果沒了,頁面像卡住了一樣,這當(dāng)然不行。
有沒有更好的方案?
??setTimeout 方案
把先前?loadingDone
?狀態(tài)不放到?getDetail
?方法中,而是單獨(dú)拿出來緊跟?this.getDetail(id)
?后面。
代碼如下:
// 定義一個(gè) timer **private timer = null;** // 請(qǐng)求下一條 id fetchPreOrNext(event: ThyArrowSwitcherEvent) { ... this.goToPreOrNextResolve(event.index, topicId).subscribe( (id: string) => { this.getDetail(id); **this.timer = setTimeout(() => { this.loadingDone = false; }, 500);** } ... ) } // 請(qǐng)求詳情數(shù)據(jù) getDetail(postId: string) { // 刪除掉該行l(wèi)oadingDone 代碼 **// this.loadingDone = false;** this.store .fetchPost(postId) .pipe( finalize(() => { this.loadingDone = true; // 記得清除 **clearTimeout(this.timer);** }) ) .subscribe(); }
這么做的含義就是,我們給到?loadingDone
?500ms 的緩沖時(shí)間,如果 500ms 內(nèi)返回?cái)?shù)據(jù)了,則沒有?loading
?的效果,如果沒有加載回來,在會(huì)顯示加載中。
一般情況如下所示:
?低速網(wǎng)絡(luò)下的效果:
這確實(shí)是一種方案,但是總感覺哪里怪怪的。
這里是個(gè)定時(shí)任務(wù)并且 500ms 后觸發(fā)。試想一種結(jié)果,當(dāng)我快速點(diǎn)擊下一條并且在 300ms 獲取到了數(shù)據(jù)并把 loadingDone 狀態(tài)置為 true, 但 500ms時(shí),loadingDone 狀態(tài)置為 false,造成假死的情況,顯然這不是我們想要的。
那這該如何解決?
???RxJS 大法
拋去使用 setTimeout
的方案,我們對(duì) getDetail
代碼改成如下的形式。
大致的思路是,將請(qǐng)求的 loading 狀態(tài)與數(shù)據(jù)獲取的狀態(tài)分離,并定義了兩個(gè)流 result$
和 showLoadingIndicator$
。
result$
流請(qǐng)求到數(shù)據(jù)之后,之后之后的一些操作, showLoadingIndicator$
流則負(fù)責(zé) loading
狀態(tài)的推送。
來看看怎么一步一步實(shí)現(xiàn)的:
首先我們定義一個(gè)請(qǐng)求的流。
const fetchPost$ = () => this.store.fetchPost(postId);
然后分別定義了兩個(gè)流?result$
?和?showLoadingIndicator$
。這里的?share()
?函數(shù)是因?yàn)闀?huì)有兩個(gè)訂閱它的地方。
const result$ = fetchPost$().pipe(share()); const showLoadingIndicator$;
然后我們來處理?showLoadingIndicator$
?流。
我們期望在 500ms 內(nèi)請(qǐng)求到的數(shù)據(jù),則不應(yīng)該展示 loading,否則,應(yīng)該展示 loading 狀態(tài)。
const showLoadingIndicator$ = timer(500).pipe(mapTo(true), takeUntil(result$))
如果在 500ms 后很快請(qǐng)求到了數(shù)據(jù),為了避免閃屏,我們需要讓?loading
?至少顯示 1s。然后使用?merge()
?合并這兩種結(jié)果。
const showLoadingIndicator$ = merge( timer(500).pipe(mapTo(true), takeUntil(result$)), combineLatest(result$, timer(1000)).pipe(mapTo(false)) ).pipe(startWith(false), distinctUntilChanged());
最后訂閱它們。
result$.subscribe( result => { // 請(qǐng)求到結(jié)果后的操作 }, error => { // TODO } ); showLoadingIndicator$.subscribe(isLoading => { // 更新 loadingDone 狀態(tài) this.loadingDone = !isLoading; });
完整的代碼如下:
// 請(qǐng)求下一條 id fetchPreOrNext(event: ThyArrowSwitcherEvent) { ... this.goToPreOrNextResolve(event.index, topicId).subscribe( (id: string) => { this.getDetail(id); } ... ) } // 請(qǐng)求詳情數(shù)據(jù) getDetail(postId: string) { const fetchPost$ = () => this.store.fetchPost(postId); const result$ = fetchPost$().pipe(share()); const showLoadingIndicator$ = merge( timer(500).pipe(mapTo(true), takeUntil(result$)), combineLatest(result$, timer(1000)).pipe(mapTo(false)) ).pipe(startWith(false), distinctUntilChanged()); result$.subscribe( result => { // TODO }, error => { // TODO } ); showLoadingIndicator$.subscribe(isLoading => { this.loadingDone = !isLoading; }); }
如果想更細(xì)致知道如何實(shí)現(xiàn)的,參考下面這篇文檔:
Loading indication with a delay and anti-flickering in RxJS
文章來源:http://www.zghlxwxcb.cn/news/detail-642837.html
本文轉(zhuǎn)載于:
https://juejin.cn/post/7176943529057321017
如果對(duì)您有所幫助,歡迎您點(diǎn)個(gè)關(guān)注,我會(huì)定時(shí)更新技術(shù)文檔,大家一起討論學(xué)習(xí),一起進(jìn)步。
?文章來源地址http://www.zghlxwxcb.cn/news/detail-642837.html
到了這里,關(guān)于記錄--Loading 用戶體驗(yàn) - 加載時(shí)避免閃爍的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!