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