React之如何在Suspense中優(yōu)雅地請求數據
什么是 Sunpense
Suspense 是 React 中的一個組件,直譯過來有懸掛的意思,能夠將其包裹的異步組件掛起,直到組件加載完成后再渲染
懶加載
在 React 組件掛載的過程中,一般是等所有的組件都加載好后一起掛載到 Dom 上, 如果出現了較大的組件,就會拖慢網頁整體的加載速度,特別是在 SPA 上會出現長時間的白屏,影響用戶體驗
于是,React 提供了lazy
Api,用于延遲加載某個組件,這樣就不會拖慢其他組件的加載速度
給 React 上的 lazy
方法傳入一個返回 Promise 或類 Promise 對象的函數,即可實現對 Promise 的結果的懶加載
恰好,ES 中的動態(tài)引入import()
就會返回一個 Promise,因此我們可將其寫成匿名函數的返回值,并作為參數傳給lazy
,下面代碼就對組件進行了懶加載
// 對Component進javascript行懶加載 const LazyComponent = React.lazy(() => import("./Component"));
掛起
但是通過lazy
得出的組件直接使用的話會在加載時報錯,還需要在外面包裹一層 Suspense 組件才能夠正常使用
// 懶加的載子組件 const SubComponent = React.lazy(() => import("./SubComponent")); // 用Suspense包裹的組件會被掛起,加載完成后再掛載 function MainComponent() { return <Suspense fallback={<div>Loading......</div>}> <SubComponent> </Suspense>; }
渲染時,React 會先將 Suspense 的 fallback 中的內容掛載,并將子組件掛起,等待加載完成后就掛載,并將 fallback 卸載 例如上面的代碼,會在 SubComponent 加載時顯示 Loading......,等待 Subcomponent 加載完成后顯示子組件內容
懶加載組件的加載過程
為啥直接使用會懶加載的組件報錯呢?我們輸出一下lazy
返回的對象看看:
原來這個對象并不是我們熟悉的組件,而是擁有狀態(tài)(status)和結果(result)的高階對象,直接使用當然會報錯啦
那么問題來了,React 為什么要返回這個對象而不是返回一個普通的組件呢?
這是為了通知 Suspense 組件!
簡單地說,Suspense 組件會嘗試渲染子組件,假如子組件未加載完成,則會轉而渲染 fallback 里的內容
那么 Suspense 又是如何判斷子組件是否加載完成的呢?對了,就是通過捕獲子組件異常!
子組件有三種狀態(tài):
- 加載中,拋出加載中的 Promise
- 加載完成,正常返回結果
- 加載異常,拋出錯誤
Suspense 會嘗試加載子組件,并通componentDidCatch過對拋出的異常進行捕獲,大概加載流程為:
這樣,通過對未加載完成的子組件不斷嘗試加載,就能夠實現“掛起”這一過程
對需要請求數據的組件使用 Suspense
現在我們知道,需要將子組件加載的狀態(tài)通知 Suspense 才能夠實等待 ajax 請求后再加載組件的效果,所以普通使用 ajax 發(fā)起請求是無法做到的
react-cache
react-cache 是 React 官方的處理數據副作用方案,里面提供了一個方法unstable_createResource
,用于創(chuàng)建一個類似懶加載組件的對象
你可以訪問這個對象上的read
方法,若訪問的數據不存在,則動態(tài)加載,并拋出 Promise,若數據存在,則正常返回數據。聽起來是不是和懶加載很像?
import getURL from "./api"; //一個封裝的請求,返回一個Promise import { unstable_createResource } from "react-cache"; // 類似import(),傳入一個返回值為PRomise的函數,生成一個擁有狀態(tài)的對象 const myData = unstable_createResource(() => { getURL(); });
// 在子組件中使用 function SubComponent() { const data = myData.read(); return <div>{data}</div>; }
這樣,父組件中包裹子組件的 Suspense 就能像檢測懶加載組件一樣檢測子組件數據加載的狀態(tài),從而展示 fallback 或子組件了
但是,react-cache 其實是一種數據緩存方案,使用 LRU(當緩存空間滿時,優(yōu)先清理最近最少使用的數據)策略緩存數據,能夠根據不同的參數緩存請求的結果,并供組件調用,如下
import getURL from "./api"; import { unstable_createResource } from "react-cache"; const myData = unstable_createResource( (param1, param2) => { getURL(param1, param2); }, (param1, param2) => param1 + param2 );
unstable_createResource
接受了兩個參數
- 第一個是產生 Promise 的函數,可以將參數傳入匿名函數后再傳入請求數據的函數(有點套娃,但是這樣傳參方便了函數內部復用請求)
- 第二個參數是個哈希函數,作用是接受與前面函數同樣的參數,然后生成一串識別碼給函數本身查詢數據是否存在(緩存)
這樣,使用時就能read
方法傳遞不同的參數,對于緩存一些高頻使用的接口的數據非常有用
如果你只是想初始化時請求、不想使用緩存,或者單純的不想引入更多庫,那么可以手寫一個能夠通知 Suspense的函數
自己寫一個加工函數
從上面兩個例子可以知道,要通知 Suspense 函數,則必須要根據 Promise 狀態(tài)進行不同的操作
為了簡化,我們的加工函數就不自己創(chuàng)建 Promise 了,而是在外面創(chuàng)建后對 Promise 進行加工
// 創(chuàng)建函數,接受一個promise function wrapPromise(promise) { let status = 0; let result; // 調用promise,并在回調中更改加工函數中的狀態(tài) const callPromise = promise.then( (res) => { status = 1; result = res; }, (err) => { status = -1; result = err; } ); // 返回一個對象,只需要提供read方法 return { read() { switch (status) { case 0: throw callPromise; case 1: return result; case -1: throw result; } }, }; }
這樣,子組件通過調用加工函數返回的對象上的read
方法,即可根據請求的狀態(tài)優(yōu)雅地加載組件了
// 獲取加工后的對象 const data = wrapPromise(promise); // 在子組件中使用 function SubComponent() { const data = myData.read(); return <div>{data}</div>; }
請求數據的時機
有了 Suspense 和加工函數,我們還要知道該在什么地方調用加工函數
在子組件中調用
warpPromise
然后請求?這是不行的,因為 Suspense 更改渲染的組件時會進行組件的重新掛載操作,相當于子組件中的所有操作都重新運行一遍。 重新運行的加工函數又會重新調用 Promise 并返回全新的對象,里面的
read
方法也會繼續(xù)拋出錯誤,從而使得 Suspense 一直渲染 fallback 中的內容在文件最外層調用?
這是可行的,組件內可以正常訪問到外面的數據。但是數據寫死了不太好控制,只能夠做一些初始化請求
在父組件中調用?
在父組件中調用,然后通過 prop 傳遞給子組件,這是可行的。并且還可以結合其他 Hook,進行靈活地操作
結論
要優(yōu)雅地在 Suspense 中請求數據,其實只要實現通過拋出異常通知 Suspense 即可
實際開發(fā)中通過 promise.then 直接操控自定義的加載遮罩也比較常見,也不必強行使用 Suspense
但是如果需要對數據進行緩存,那么在使用 react-cache 時順手用上 Suspense 就更方便了
到此這篇關于React之如何在Suspense中優(yōu)雅地請求數據的文章就介紹到這了,更多相關在Suspense中請求數據內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
使用react-beautiful-dnd實現列表間拖拽踩坑
相比于react-dnd,react-beautiful-dnd更適用于列表之間拖拽的場景,本文主要介紹了使用react-beautiful-dnd實現列表間拖拽踩坑,感興趣的可以了解一下2021-05-05react-redux action傳參及多個state處理的實現
本文主要介紹了react-redux action傳參及多個state處理的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-07-07React Native模塊之Permissions權限申請的實例相機
這篇文章主要介紹了React Native模塊之Permissions權限申請的實例相機的相關資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-09-09