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