本篇内容主要讲解“react.js中swr的原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“react.js中swr的原理是什么”吧!
useSWR
是 react hooks 中一个比较有意思的组件,既可以作为请求库,也可以作为状态管理的缓存用,SWR 的名字来源于“stale-while-revalidate”, 是在HTTP RFC 5861标准中提出的一种缓存更新策略 :
首先从缓存中取数据,然后去真实请求相应的数据,最后将缓存值和最新值做对比,如果缓存值与最新值相同,则不用更新,否则用最新值来更新缓存,同时更新UI展示效果。
useSWR
可以作为请求库来用:
//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 ) // ... }
此外,因为相同的 key
总是返回相同的实例,在 useSWR
中只保存了一个 cache
实例,因此 useSWR
也可以当作全局的状态管理机。比如可以全局保存用户名称 :
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 的用法不是本文的重点,具体可以看文档,本文用一个例子来引出对于 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都输出了{a:'500 is ok '}
原因也很简单,在swr默认的时间内(默认是 2000
毫秒),对于同一个 useSWR
的 key
,这里的 key
是 ‘/api/user’
会进行重复值清除, 只始终 2000
毫秒内第一个 key
的fetcher
函数来进行缓存更新。
带着这个例子,我们来深入读读 swr 的源码
我们从 useSWR
的 API 入手,来读一读 swr 的源码。首先在 swr 中本质是一种内存中的缓存更新策略,所以在 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
类的定义,本质其实很简单,维护了一个 map
对象,以 key
为索引,其中key
可以是字符串,函数或者数组,将 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
是字符串,那么这个字符串就是序列化后的 key
如果传入的 key
是函数,那么执行这个函数,返回的结果就是序列化后的 key
如果传入的 key
是数组,那么通过 hash
方法(类似 hash
算法,数组的值序列化后唯一)序列化后的值就是 key
。
此外,在 cache
类中,将这个保存了 key
和 value
信息的缓存对象 map
,保存在实例对象 this.__cache
中,这个 this.__cache
对象就是一个 map
,有set get等方法。
在swr中,可以配置各种事件,当事件被触发时,会触发相应的重新请求或者说更新函数。swr对于这些事件,比如断网重连,切换 tab
重新聚焦某个 tab
等等,默认是会自动去更新缓存的。
在swr中对事件处理的代码为:
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
事件中保存了相应的更新缓存函数,当页面触发事件visibilitychange(显示隐藏)、focus(页面聚焦)以及online(断网重连)的时候会触发事件,自动更新缓存 。
useSWR
是swr的主体函数,决定了如何缓存以及如何更新,我们先来看 useSWR
的入参和形参。
入参:
key
: 一个唯一值,可以是字符串、函数或者数组,用来在缓存中唯一标识 key
fetcher
: (可选) 返回数据的函数
options
: (可选)对于 useSWR
的一些配置项,比如事件是否自动触发缓存更新等等。
出参:
data
: 与入参 key
相对应的,缓存中相应 key
的 value
值
error
: 在请求过程中产生的错误等
isValidating
: 是否正在请求或者正在更新缓存中,可以做为 isLoading
等标识用。
mutate(data?, shouldRevalidate?)
: 更新函数,手动去更新相应 key
的 value
值
从入参到出参,我们本质在做的事情,就是去控制 cache
实例,这个 map
的更新的关键是:
什么时候需要直接从缓存中取值,什么时候需要重新请求,更新缓存中的值。
const stateRef = useRef({ data: initialData, error: initialError, isValidating: false }) const CONCURRENT_PROMISES = {} //以key为键,value为新的通过fetch等函数返回的值 const CONCURRENT_PROMISES_TS = {} //以key为键,value为开始通过执行函数获取新值的时间戳
下面我们来看,缓存更新的核心函数: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
到此,相信大家对“react.js中swr的原理是什么”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。