您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“react.js中swr的原理是什么”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“react.js中swr的原理是什么”吧!
useSWR
是 react hooks 中一個(gè)比較有意思的組件,既可以作為請(qǐng)求庫(kù),也可以作為狀態(tài)管理的緩存用,SWR 的名字來(lái)源于“stale-while-revalidate”, 是在HTTP RFC 5861標(biāo)準(zhǔn)中提出的一種緩存更新策略 :
首先從緩存中取數(shù)據(jù),然后去真實(shí)請(qǐng)求相應(yīng)的數(shù)據(jù),最后將緩存值和最新值做對(duì)比,如果緩存值與最新值相同,則不用更新,否則用最新值來(lái)更新緩存,同時(shí)更新UI展示效果。
useSWR
可以作為請(qǐng)求庫(kù)來(lái)用:
//fetch import useSWR from 'swr' import fetch from 'unfetch' const fetcher = url => fetch(url).then(r => r.json()) function App () { const { data, error } = useSWR('/api/data', fetcher) // ... } //axios const fetcher = url => axios.get(url).then(res => res.data) function App () { const { data, error } = useSWR('/api/data', fetcher) // ... } //graphql import { request } from 'graphql-request' const fetcher = query => request('https://api.graph.cool/simple/v1/movies', query) function App () { const { data, error } = useSWR( `{ Movie(title: "Inception") { releaseDate actors { name } } }`, fetcher ) // ... }
此外,因?yàn)橄嗤?key
總是返回相同的實(shí)例,在 useSWR
中只保存了一個(gè) cache
實(shí)例,因此 useSWR
也可以當(dāng)作全局的狀態(tài)管理機(jī)。比如可以全局保存用戶名稱 :
import useSWR from 'swr'; function useUser(id: string) { const { data, error } = useSWR(`/api/user`, () => { return { name: 'yuxiaoliang', id, }; }); return { user: data, isLoading: !error && !data, isError: error, }; } export default useUser;
具體的 swr 的用法不是本文的重點(diǎn),具體可以看文檔,本文用一個(gè)例子來(lái)引出對(duì)于 swr 原理的理解:
const sleep = async (times: number) => { return new Promise(resolve => { setTimeout(() => { resolve(); }, times); }); }; const { data: data500 } = useSWR('/api/user', async () => { await sleep(500); return { a: '500 is ok' }; }); const { data: data100 } = useSWR('/api/user', async () => { await sleep(100); return { a: '100 is ok' }; });
上述的代碼中輸出的是 data100 和 data500 分別是什么?
答案是:
data100和data500都輸出了{(lán)a:'500 is ok '}
原因也很簡(jiǎn)單,在swr默認(rèn)的時(shí)間內(nèi)(默認(rèn)是 2000
毫秒),對(duì)于同一個(gè) useSWR
的 key
,這里的 key
是 ‘/api/user’
會(huì)進(jìn)行重復(fù)值清除, 只始終 2000
毫秒內(nèi)第一個(gè) key
的fetcher
函數(shù)來(lái)進(jìn)行緩存更新。
帶著這個(gè)例子,我們來(lái)深入讀讀 swr 的源碼
我們從 useSWR
的 API 入手,來(lái)讀一讀 swr 的源碼。首先在 swr 中本質(zhì)是一種內(nèi)存中的緩存更新策略,所以在 cache.ts
文件中,保存了緩存的 map
。
class Cache implements CacheInterface { constructor(initialData: any = {}) { this.__cache = new Map(Object.entries(initialData)) this.__listeners = [] } get(key: keyInterface): any { const [_key] = this.serializeKey(key) return this.__cache.get(_key) } set(key: keyInterface, value: any): any { const [_key] = this.serializeKey(key) this.__cache.set(_key, value) this.notify() } keys() { } has(key: keyInterface) { } clear() { } delete(key: keyInterface) { } serializeKey(key: keyInterface): [string, any, string] { let args = null if (typeof key === 'function') { try { key = key() } catch (err) { // dependencies not ready key = '' } } if (Array.isArray(key)) { // args array args = key key = hash(key) } else { // convert null to '' key = String(key || '') } const errorKey = key ? 'err@' + key : '' return [key, args, errorKey] } subscribe(listener: cacheListener) { if (typeof listener !== 'function') { throw new Error('Expected the listener to be a function.') } let isSubscribed = true this.__listeners.push(listener) return () => { //unsubscribe } } // Notify Cache subscribers about a change in the cache private notify() { }
上述是 cache
類的定義,本質(zhì)其實(shí)很簡(jiǎn)單,維護(hù)了一個(gè) map
對(duì)象,以 key
為索引,其中key
可以是字符串,函數(shù)或者數(shù)組,將 key
序列化的方法為:serializeKey
serializeKey(key: keyInterface): [string, any, string] { let args = null if (typeof key === 'function') { try { key = key() } catch (err) { // dependencies not ready key = '' } } if (Array.isArray(key)) { // args array args = key key = hash(key) } else { // convert null to '' key = String(key || '') } const errorKey = key ? 'err@' + key : '' return [key, args, errorKey] }
從上述方法的定義中我們可以看出:
如果傳入的 key
是字符串,那么這個(gè)字符串就是序列化后的 key
如果傳入的 key
是函數(shù),那么執(zhí)行這個(gè)函數(shù),返回的結(jié)果就是序列化后的 key
如果傳入的 key
是數(shù)組,那么通過(guò) hash
方法(類似 hash
算法,數(shù)組的值序列化后唯一)序列化后的值就是 key
。
此外,在 cache
類中,將這個(gè)保存了 key
和 value
信息的緩存對(duì)象 map
,保存在實(shí)例對(duì)象 this.__cache
中,這個(gè) this.__cache
對(duì)象就是一個(gè) map
,有set get等方法。
在swr中,可以配置各種事件,當(dāng)事件被觸發(fā)時(shí),會(huì)觸發(fā)相應(yīng)的重新請(qǐng)求或者說(shuō)更新函數(shù)。swr對(duì)于這些事件,比如斷網(wǎng)重連,切換 tab
重新聚焦某個(gè) tab
等等,默認(rèn)是會(huì)自動(dòng)去更新緩存的。
在swr中對(duì)事件處理的代碼為:
const revalidate = revalidators => { if (!isDocumentVisible() || !isOnline()) return for (const key in revalidators) { if (revalidators[key][0]) revalidators[key][0]() } } // focus revalidate window.addEventListener( 'visibilitychange', () => revalidate(FOCUS_REVALIDATORS), false ) window.addEventListener('focus', () => revalidate(FOCUS_REVALIDATORS), false) // reconnect revalidate window.addEventListener( 'online', () => revalidate(RECONNECT_REVALIDATORS), false )
上述 FOCUS_REVALIDATORS
, RECONNECT_REVALIDATORS
事件中保存了相應(yīng)的更新緩存函數(shù),當(dāng)頁(yè)面觸發(fā)事件visibilitychange(顯示隱藏)、focus(頁(yè)面聚焦)以及online(斷網(wǎng)重連)的時(shí)候會(huì)觸發(fā)事件,自動(dòng)更新緩存 。
useSWR
是swr的主體函數(shù),決定了如何緩存以及如何更新,我們先來(lái)看 useSWR
的入?yún)⒑托螀ⅰ?/p>
入?yún)?
key
: 一個(gè)唯一值,可以是字符串、函數(shù)或者數(shù)組,用來(lái)在緩存中唯一標(biāo)識(shí) key
fetcher
: (可選) 返回?cái)?shù)據(jù)的函數(shù)
options
: (可選)對(duì)于 useSWR
的一些配置項(xiàng),比如事件是否自動(dòng)觸發(fā)緩存更新等等。
出參:
data
: 與入?yún)?key
相對(duì)應(yīng)的,緩存中相應(yīng) key
的 value
值
error
: 在請(qǐng)求過(guò)程中產(chǎn)生的錯(cuò)誤等
isValidating
: 是否正在請(qǐng)求或者正在更新緩存中,可以做為 isLoading
等標(biāo)識(shí)用。
mutate(data?, shouldRevalidate?)
: 更新函數(shù),手動(dòng)去更新相應(yīng) key
的 value
值
從入?yún)⒌匠鰠?,我們本質(zhì)在做的事情,就是去控制 cache
實(shí)例,這個(gè) map
的更新的關(guān)鍵是:
什么時(shí)候需要直接從緩存中取值,什么時(shí)候需要重新請(qǐng)求,更新緩存中的值。
const stateRef = useRef({ data: initialData, error: initialError, isValidating: false }) const CONCURRENT_PROMISES = {} //以key為鍵,value為新的通過(guò)fetch等函數(shù)返回的值 const CONCURRENT_PROMISES_TS = {} //以key為鍵,value為開(kāi)始通過(guò)執(zhí)行函數(shù)獲取新值的時(shí)間戳
下面我們來(lái)看,緩存更新的核心函數(shù):revalidate
// start a revalidation const revalidate = useCallback( async ( revalidateOpts= {} ) => { if (!key || !fn) return false revalidateOpts = Object.assign({ dedupe: false }, revalidateOpts) let loading = true let shouldDeduping = typeof CONCURRENT_PROMISES[key] !== 'undefined' && revalidateOpts.dedupe // start fetching try { dispatch({ isValidating: true }) let newData let startAt if (shouldDeduping) { startAt = CONCURRENT_PROMISES_TS[key] newData = await CONCURRENT_PROMISES[key] } else { if (fnArgs !== null) { CONCURRENT_PROMISES[key] = fn(...fnArgs) } else { CONCURRENT_PROMISES[key] = fn(key) } CONCURRENT_PROMISES_TS[key] = startAt = Date.now() newData = await CONCURRENT_PROMISES[key] setTimeout(() => { delete CONCURRENT_PROMISES[key] delete CONCURRENT_PROMISES_TS[key] }, config.dedupingInterval) } const shouldIgnoreRequest = CONCURRENT_PROMISES_TS[key] > startAt || (MUTATION_TS[key] && (startAt
到此,相信大家對(duì)“react.js中swr的原理是什么”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。