Angular:使用单个 Rx 运算符进行异步渲染
这篇文章最初发表于Angular Bites
按照我的意思,异步渲染的概念很简单:在屏幕上渲染项目的过程是分散的,这样浏览器就不会阻塞,直到所有项目都渲染完毕。
它的工作原理如下:渲染第一个项目,然后稍等片刻,再渲染下一个项目,依此类推。在此期间,浏览器可以执行循环中所有其他预定的事件,然后再让它再次渲染。
何时以及为何应该使用它,有时
这在什么时候尤其有效?
- 如果我们要渲染特别长且繁重的列表
- 如果列表中的每个项目占用大量页面空间
为什么?因为你的应用看起来会更快。它实际上并不会更快,但你的用户会感觉到它更快。这就足够了。
单一操作员方法
过去我曾用各种方法解决过这个问题,正如我在如何在 Angular 中渲染大型列表中所描述的那样。
这次我想到了一个单一的操作符,它将按顺序分散数组子集的渲染过程。
我们称这个运算符为lazyArray
。它支持两个参数:
delayMs
= 浏览器渲染下一个数组之前应等待多长时间concurrency
= 一次渲染多少个项目
只要给我看代码就行了,Giancarlo!
好的,就是这样:
export function lazyArray<T>(
delayMs = 0,
concurrency = 2
) {
let isFirstEmission = true;
return (source$: Observable<T[]>) => {
return source$.pipe(
mergeMap((items) => {
if (!isFirstEmission) {
return of(items);
}
const items$ = from(items);
return items$.pipe(
bufferCount(concurrency),
concatMap((value, index) => {
const delayed = delay(index * delayMs);
return scheduled(of(value), animationFrameScheduler).pipe(delayed);
}),
scan((acc: T[], steps: T[]) => {
return [ ...acc, ...steps ];
}, []),
tap((scannedItems: T[]) => {
const scanDidComplete = scannedItems.length === items.length;
if (scanDidComplete) {
isFirstEmission = false;
}
}),
);
}),
);
};
}
用法
使用它非常简单,就像使用其他运算符一样:
@Component({ ... })
export class MyComponent {
items$ = this.service.items$.pipe(
lazyArray()
);
}
让我们来分析一下,好吗?
我们希望跟踪它是否是第一次发射。我们只想在第一次进行延迟渲染:
let isFirstEmission = true;
我们将数组转换为项目流:
const items$ = from(items);
我们根据并发性将项目数量收集到一个数组中:
bufferCount(concurrency),
我们根据延迟安排渲染,然后根据项目的索引逐步增加延迟:
concatMap((value, index) => {
const delayed = delay(index * delayMs);
return scheduled(of(value), animationFrameScheduler).pipe(delayed);
})
我们不断将处理过的项目收集到一个数组中:
scan((acc: T[], steps: T[]) => {
return [ ...acc, ...steps ];
}, [])
最后,我们检查已处理项目的数量是否与初始列表一样长。通过这种方式,我们可以了解第一次发射是否完成,以及如果我们将标志设置为false
:
tap((scannedItems: T[]) => {
const scanDidComplete = scannedItems.length === items.length;
if (scanDidComplete) {
isFirstEmission = false;
}
})
演示
我之所以想到这个,是因为我的应用程序 Formtoro 在启动时会加载大量数据,从而一次性渲染大量 Stencil 组件。
它运行得不太好,卡顿很严重。我不太喜欢它,所以我找到了解决办法。我来给你展示一下区别:
不带lazyArray
运算符:
带lazyArray
运算符:
这种方法对我来说非常有效,但对你来说可能就不行了。如果你需要帮助,可以给我发邮件。
再见!
如果你喜欢这篇文章,请在Twitter上关注我,或者查看我的新博客Angular Bites
鏂囩珷鏉ユ簮锛�https://dev.to/angular/angular-async-rendering-with-a-single-rx-operator-4d6d