原文链接:https://zhuanlan.zhihu.com/p/90660704 作者:shud.in
数据依赖关系其实是一个 DAG(有向无环图)。有些数据依赖于其他,有的则无依赖性:
DAG:
对数据的请求则是对这个有向无环图的遍历。最高效的请求方式一定是在拓扑序
上尽可能地并行(每当一个数据的依赖都就绪时,立即发起请求)。
仔细想想,大部分时候(请求并不复杂时),我们都用 Promise.all
来描述这个 DAG。举例来说:
Promise.all([fetchA(), fetchB()]).then([a, b] => fetchC(a, b))
有 A,B,C 三个资源,C 依赖 A 与 B 的结果,那么我们偶尔会写如上代码。实际上描述了如下 DAG:
但是当依赖复杂起来……例如:
有 A,B,C,D 四个资源,C 依赖 A,D 依赖 B 与 C……最并行的写法可能是这样的:
(以上假设在 React 中,我们需要尽快渲染 A,B,C,D)
是不是非常头疼(飞面神教流 React 写法)?可以当笔试题了。
但在 SWR 中,你只需要这么写:
依然满足了最大的并行性,与上面完全等价。只需要描述 DAG 中,每一个点的被指向边即可(数据依赖)。
SWR 是如何做到这一点的呢?
这里有一个解释:每次渲染的时候,SWR 会试着执行 key 函数(例如 ()=> '/api/c?a=' + A.id
),如果这个函数抛出异常,那么就意味着它的依赖还没有就绪(A === undefined
),SWR 将暂停这个数据的请求。在任一数据完成加载时,由于 setState
触发重渲染,上述 Hooks 会被重选执行一遍(再次检查数据依赖是否就绪),然后对就绪的数据发起新的一轮请求。
(以上翻译自 https://twitter.com/shuding_/status/1189607308931653632)
题外话
最近推特上有许多关于 fetch-as-you-render
的讨论,许多人陷入了对模式本身的争论中。
但这篇文章确实提出 fetch-as-you-render 一个非常大的优势(尽可能避免了 waterfall)。对于 waterfall,包括 React Suspense 也没有很好的方案建议:https://reactjs.org/docs/concurrent-mode-suspense.html#for-library-authors 。
另外对于 fetch-as-you-render 的弱势(需要在渲染时才能开始加载数据),ZEIT 的解决办法是配合 Next.js,对第一轮数据(无其他依赖)自动生成并插入 <link link rel=preload>
标签,改进效果非常明显(https://twitter.com/rauchg/status/1189277551321079810)。