原文鏈接: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)。