递延数据
有时您想检索一些可选数据,而不会阻止包含应用程序关键数据的页面的其余部分。此可选数据的示例是对博客文章的评论,该博客文章在主要内容,购物车上推荐的产品,最新搜索等。
。React Router 6引入了“ deferred” API,该API允许您在调用加载程序时“等待”关键数据和“ defer”可选数据。
最好的部分是,您可以通过添加或删除解决数据的承诺中的等待关键字来在一种模式或另一种模式之间切换。 Ryan Florence在他的演讲“ When to Fetch”中对这种机制做了很好的解释(严重的是,如果您还没有看过,这是一个了不起的演讲,请在阅读这篇文章后观看并观看它!)
)))我从React Router examples文件夹和the documentation中看了延迟的演示应用程序,以发现其所有潜力,但是,我找不到fetch
的榜样,所以我决定在这里尝试一下,在这里玩它。是我发现的。
将defer
与获取
我创建了一个mock server with MSW来模拟提取延迟以及显示示例中的相同文本。
这是我的第一个天真尝试:
// Don't copy paste! bad code! keep on reading...
export const loader = async () => {
return defer({
critical1: await fetch('/test?text=critical1&delay=250').then(res => res.json()),
critical2: await fetch('/test?text=critical2&delay=500').then(res => res.json()),
lazyResolved: await fetch('/test?text=lazyResolved&delay=0').then(res => res.json()),
lazy1: fetch('/test?text=lazy1&delay=1000').then(res => res.json()),
lazy2: fetch('/test?text=lazy2&delay=1500').then(res => res.json()),
lazy3: fetch('/test?text=lazy3&delay=2000').then(res => res.json()),
lazyError: fetch('/test?text=lazy3&delay=2500').then(res => {
throw Error('Oh noo!')
}),
});
}
那么我们在这里做什么?
首先,从普通装载机中返回“裸露”,因为这是装载机的期望和反应路由器会为您解开响应,但是,defer
接受 value>值或 promist 将其解析为值,因此要获得值,我们需要手动解开提取响应。
提取很棒!但是,您必须做一些额外的工作,包括抛出错误和如上所述。使用平台yay! ð
这是结果:
直到我打开网络选项卡和某些东西看起来不太正确ðÖ
之前,一切看起来都很棒。使用await
关键字的关键数据的前两个请求正在瀑布中发生,而不是像其他情况一样并行!实际上,如果您在登录对象中添加所有呼叫,它们都在瀑布中发生,什么都可以!
-
Ryan对我们说谎吗?
-
这是一个错误吗? ð
-
瀑布中需要发生关键数据吗? -
nope !,当然不是!事实证明,我忘记了 javascript如何工作
发生瀑布是因为每次创建一个单独的await
时,它都会暂停执行,然后再继续下一行,然后在发射下一个提取之前。
我们要避免瀑布的事情是同时射击所有这些提取请求,而仅对响应而不是实际的fetch
。
您越早启动提取,越好,因为它越早开始,它越早完成
1111 @TkDodo
为了实现此目的
// You can copy this one if you want
export const loader = async () => {
// fire them all at once
const critical1Promise = fetch('/test?text=critical1&delay=250').then(res => res.json());
const critical2Promise = fetch('/test?text=critical2&delay=500').then(res => res.json());
const lazyResolvedPromise = fetch('/test?text=lazyResolved&delay=100').then(res => res.json());
const lazy1Promise = fetch('/test?text=lazy1&delay=500').then(res => res.json());
const lazy2Promise = fetch('/test?text=lazy2&delay=1500').then(res => res.json());
const lazy3Promise = fetch('/test?text=lazy3&delay=2500').then(res => res.json());
const lazyErrorPromise = fetch('/test?text=lazy3&delay=3000').then(res => {
throw Error('Oh noo!')
});
// await for the response
return defer({
critical1: await critical1Promise,
critical2: await critical2Promise,
lazyResolved: lazyResolvedPromise,
lazy1: lazy1Promise,
lazy2: lazy2Promise,
lazy3: lazy3Promise,
lazyError: lazyErrorPromise
})
}
您也可以做Promise.all()
,但是在上面的示例中,更容易理解发生了什么。
这是网络标签现在的样子,美丽的平行绿色条。
现在我们修复了瀑布,让我们延迟玩耍,并探索defer的几个有趣的功能:
关键数据
关键数据使用“等待”的关键字,因此加载程序和React路由器将等到数据准备就绪之前(没有加载旋转器ð)。
如果关键数据(使用等待)返回错误,会发生什么? ðÖ好吧,装载机将扔到最近的错误边界并破坏整个页面或整个路线段。
如果您想优雅地失败并且不想销毁整个页面,请删除等待,这基本上是告诉React Router,嘿!我不在乎这些数据是否失败,这不是那么重要(关键),因此请显示局部错误边界。这正是lazyError
在第一个示例中所做的。
懒惰解决了
我们没有在lazyResolved
字段上使用await
,但是我们根本看不到加载旋转器。那个怎么样?这实际上是递送的惊人功能,如果您的可选数据快(比关键数据快),那么您根本看不到旋转器,因为当关键数据完成和第一个渲染发生时,您的承诺将得到解决:
最慢的关键数据延迟是500ms
,但是lazyResolved
数据仅处理100ms
,因此到解决critical2
IS解决时,lazyResolved
Promise已经解决了,并且数据可以立即可用。
关于延期的最好的事情是,您不必选择如何获取数据,如果快速显示可选数据,或者如果速度很慢,则显示加载旋转器。
您可以围绕更改延迟,增加/减少时间查看旋转器是否显示出来。例如,如果我们将lazyResolved
的延迟增加到3500ms
,我们将看到一个加载旋转器。
结论
defer是一个很棒的API,我花了一段时间才能理解它并使其与Fetch一起使用,但它是提高性能,页面可靠性和开发人员体验的惊人工具。
示例的源代码可在此处提供: