swr & ahooks:
介绍
- swr原指
stale-while-revalidate
,是指一种http的缓存失效策略,属于Cache-Control
参数(浏览器兼容性较差)。根据这种策略的思想,开发了js库-zeit/swr(本文均指swr库)。
swr
一、swr解决的问题:
1、多组件共用同一数据时,会在父组件请求一次,然后props形式 传给各个组件。
- 解决方式:封装useSWR()为hooks,子组件使用时直接调用该hooks,即使多次调用也只会请求一次(因为使用相同的key,会被去重、共享、缓存)。重新聚焦页面或标签页切换时,会重新请求。
2、重新请求时机:
- 默认在 切换浏览器标签页/电脑休眠后, 重新聚焦页面时,自动重新请求
revalidateOnFocus=true
。 - 使用useSWR() hooks的组件,重新显示在屏幕上时,自动重新请求。
- 指定
refreshInterval
定时请求时间,重新请求。 - 断网重连时,自动重新请求。
revalidateOnReconnect=true
3、更方便的预加载,不需要手写数据提前请求,swr内置缓存。
- 可以将调用了useSWR()的组件,设置display:none,即可实现数据预加载。
function App () {
const [pageIndex, setPageIndex] = useState(0);
return <div>
<Page index={pageIndex}/>
<div style={{ display: 'none' }}><Page index={pageIndex + 1}/></div>
<button onClick={() => setPageIndex(pageIndex - 1)}>Previous</button>
<button onClick={() => setPageIndex(pageIndex + 1)}>Next</button>
</div>
}
4、修改请求参数后,重新请求,需要多次触发。保留分页状态困难。
- 解决方式: 使用
useSWRInfinite();
通过一个hooks触发多个请求。返回值比useSWR()多了size
和setSize
。 - 通过
useSWRInfinite();
的size
和setSize
可以在返回表单页面时,自动恢复原来的分页状态。
5、可结合React.Suspense
使用。【实验功能】
- 包含swr的组件渲染前,懒加载
<Suspense>
的fallback
。
6、性能优化。
- 主要表现在两个方面:内置缓存;重复数据删除(相同key请求同一时间只发生一次)。
- 默认使用stable-hash深度比较数据更改,如果
data
值未改变,则不会触发重新渲染。 useSWR()
返回的data
、error
和isValidating
三个值独立更新,同时使用会触发多次渲染(至少4次),推荐尽量少的同时使用三种状态。- 使用 tree-shaking,按需引入模块。
二、swr使用方法:
1、useSWR(): key
为fetcher
默认参数。
const { data, error, isValidating, mutate } = useSWR(key,fetcher,options);
- 如果
fetcher
需要多个参数,key
可设为数组,保证每次请求key
都为最新值。 key
也可设为对象。
2、全局配置: options 等同于 useSWR 第三个参数,多了provider参数。
- 全局共用相同 fetcher,
<Component/>
内调用useSWR时,不必再传fetcher。
<SWRConfig value={options}>
<Component/>
</SWRConfig>
3、禁用所有自动请求:(使用于静态数据请求)
import useSWRImmutable from 'swr/immutable';
useSWRImmutable(key, fetcher, options) //与useSWR共用相同fetcher
//等同于:
useSWR(key, fetcher, {
revalidateIfStale: false, // swr在挂载并存在陈旧数据(缓存)时是否应重新请求
revalidateOnFocus: false,
revalidateOnReconnect: false
})
4、按需请求:
key
为null
时,不请求。
const { data } = useSWR(() => shouldFetch ? '/api/data' : null, fetcher)
5、相同接口重新验证/调用:
mutate(key)
:key
为需要验证useSWR()
的key
值。
const { mutate } = useSWRConfig();
mutate('/api/user');
- 绑定的
mutate(data)
:data
为更新的本地数据,并重新验证(请求)。
const { data, mutate } = useSWR('/api/user', fetcher);
mutate({ ...data, name: newName })
6、多种预请求方法:
- 顶级页面预请求:
HTML
的<head>
标签内设置ref="proload"
。
<link rel="preload" href="/api/data" as="fetch" crossorigin="anonymous">
- 手动预请求: 使用全局的
mute
重新请求和设置缓存。
import { mutate } from 'swr';
mutate('/api/data', fetch('/api/data').then(res => res.json()))
- 数据预填充:
fallbackData
内设置请求响应前的已有数据。
useSWR('/api/data', fetcher, { fallbackData: prefetchedData })
7、中间件【在useSWR()前后执行的逻辑】
- 全局或指定
options
内的use
参数设置中间件数组,数组顺序为中间件执行顺序。
8、swr默认使用全局缓存,在所有组件之间存储和共享数据。【也可自定义Provider,实现该功能】
options
设置provider
参数:{ provider: () => new Map() }
- 使用自定义的缓存时,访问缓存:
const { cache } = useSWRConfig()
cache.get(key) // 获取 key 的当前数据。
cache.clear() // 清除所有缓存。SWR 将在重新渲染时重新请求。
*9、使用React Native时,页面聚焦可自定义。
- 修改
options
的initFocus
和initFocus
参数。
ahooks
一、useRequest
ahooks的useRequest缓存等功能借鉴了swr的特性。
useRequest()
默认参数及返回,与useSWR()
类似:const { data, error, loading } = useRequest(getUsername);
其中
useRequest()
第一个参数为fetcher
。(useSWR()
为key
)
1、支持手动请求
默认为自动请求,返回data
值;设置options.manual=true
,通过返回的run()
/runAsync()
手动触发请求。
- 适用场景:webgis开发中,点击地图内部元素查询详情等。
const {
data, loading, run, runAsync, params, cancel,
refresh, // 使用上一次的params重新调用run
refreshAsync,
mutate, //直接修改data
} = useRequest(changeUsername, {
manual: true,
defaultParams: params, // 首次执行的默认参数
onBefore:()=>{}, // 请求前触发
onSuccess: (result, params) => {}, // 请求成功触发
onError:()=>{}, // 请求失败触发
onFinally:()=>{} // 请求后触发
});
function changeUsername(name){} // fetchrer 接收run传递的参数
run('name'); //执行
refresh; // 执行刷新,传递参数仍为'name'
- 也可使用
runAsync()
,返回Promise
异步函数,但无法通过onSuccess
和onError
捕捉处理结果。 mutate()
能够立即修改useRequest()
返回的data
数据,支持mutate(newData)
和mutate(oldData=>newData)
两种写法。
2、支持请求取消
自动取消:
- 使用
useRequest()
的组件卸载时,取消正在进行的请求。【非常适用于可视化项目组件中的地图图层请求】 - 竞态取消,上一次请求还未返回时,再次发起请求,会取消上一次的请求。
手动取消:
- 调用
useRequest
返回的cancel
函数。
3、参数管理
- 若触发请求:
run(1,2,3)
,则useRequest()
返回的params
等于[1,2,3]
。只记录当前调用服务的参数,并不根据新的参数重新调用服务。
4、交互优化
useRequest
第二参数的其他配置项:
- 延迟loading渲染:延迟
loading
变为true
的时间,避免页面上闪烁出"loading…"界面。设置loadingDelay
,值为延迟时间(默认单位ms)。 - 轮询请求:设置
pollingInterval
,值为轮询请求间隔时间(默认单位ms),通过cancel
取消,run/runAsync
启动。 - 轮询错误重试:设置
pollingErrorRetryCount
,值为轮询错误重试次数。设置pollingWhenHidden
,值为boolean
,表示在页面隐藏时,是否继续轮询。【manual=true
时,初始化不启动轮询,需要run
触发。原理:请求完成后,等待pollingInterval
时间,再发起下一次请求。】 - 错误重试:设置
retryCount
,值为错误重试次数。设置retryInterval
,值为重试时间间隔(单位ms)。默认值为:第n次请求时,2的n次方,最大值不超过30s。 - 请求准备:设置
ready
为false
时,请求永远不会发出。 - 依赖刷新:设置
refreshDeps
为any[]
时,监听数组内元素变化,变化后重新出发请求(等同于useEffect
第二参数)。 - 屏幕聚焦请求:默认关闭,设置
refreshOnWindowFocus
为true
开启聚焦重新请求;设置focusTimespan
,值为时间(单位ms),表示和上次请求间隔大于该时间,则重新请求。 - 防抖:设置
debounceWait
,值为时间(单位ms),频繁触发run
函数,只在最后一次触发结束后等待该时间再执行。debounceLeading:boolean
,延迟开始前调用;debounceTrailing:boolean
,延迟开始后调用;debounceMaxWait:number
,允许被延迟的最大值;防抖等待执行的函数可被cancel
中止。 - 节流:设置
throttleWait
,值为时间(单位ms),该时间内频繁触发run函数,仅执行一次。throttleLeading:boolean
,节流开始前调用;throttleTrailing:boolean
,节流开始后调用;同样可被cancel
中止。
5、swr缓存机制
swr机制:优先使用缓存数据,后台调用请求,请求到新数据后触发更新。
- 设置
cacheKey : string
,值为唯一请求标识。staleTime:number
值为时间(单位ms),该时间内值为新鲜的,不会重新请求;cacheTime: number
值为数据缓存时间,超过该时间则清空该条缓存数据。 cacheKey
的值全局共享,相同cacheKey
在全局同时只会有一个在发起请求,其他的cacheKey
共享返回数据,且内容会同步接受。- 参数缓存:设置
cacheKey
后,useRequest
返回的params
即为上次调用时的参数。 - 删除缓存:
clearCache
方法删除指定cacheKey的缓存数据。import {clearCache} from 'ahooks'
,若clearCache
入参为空,则清空所有缓存数据;若入参为string[]
,清空数组内所有cacheKey
。 - 自定义缓存方式:setCache和getCache配套使用;自定义缓存时,
clearCache
方法不生效。
const cacheKey = 'setCache-demo';
const { data, loading } = useRequest(getArticle, {
cacheKey,
setCache: (data) => localStorage.setItem(cacheKey, JSON.stringify(data)),
getCache: () => JSON.parse(localStorage.getItem(cacheKey) || '{}'),
});
- 【注】:只有成功请求的数据才会被缓存;缓存数据包括
data
和params
。
二、场景应用-常用hooks
只介绍应用场景,不介绍具体使用方式。
useAntdTable
:封装了antd中的Form
与Table
的联动逻辑。如:传给fetcher
分页参数,以及表单筛选参数;支持表单缓存、表单验证、参数初始化等功能。``useInfiniteScroll
:无限滚动逻辑,向下滚动时发送请求。移动端列表显示场景,较为常用。usePagination
:类似useRequest
,会给fetcher传入current
,pageSize
等参数,方便分页展示。useDynamicList
:对列表内容及每一条的增删改查,包含列表条目拖拽等功能。useSelections
:封装了antd中Checkbox
的联动逻辑。
三、场景应用-其他hooks
仅介绍用处。
useFusionTable
:封装Fusion中的Form
与Table
逻辑。useVirtualList
:大数据量列表渲染。内部监听滚动事件,实时计算列表可视范围,渲染可视范围内的列表条目,使得大数据量展示流畅。useHistoryTravel
:对value
进行历史变化状态管理,包含撤销,重置,前进,跳转指定步数等功能。useNetwork
:获取网络信息,包含延迟、带宽估算、是否在线、网络类型(2g/3g/4g)等。useCountDown
:倒计时管理。获取到某个时间(几月几日几点)的剩余时间,或倒计时指定时间(几秒)。useCounter
:计数器。提供+1、-1、重置、设置数值等方法。useTextSelection
:获取用户当前选取文本的内容及位置(坐标)。useWebSocket
:处理websocket的hooks。提供了连接状态相关属性,及状态回调。
生命周期相关hooks:
useMount
: 相当于useEffect(()=>{},[])
。useUnmount
:相当于useEffect(()=>()=>{ /*卸载函数*/ },[])
。useUnmountedRef
:获取当前组件是否已经卸载的boolean
值。
四、状态管理hooks
常用:
useSetState
:适用多个状态作为object
的属性统一管理,只传入更新的属性即可。useToggle
:只能在两个值之间切换(useBoolean
的扩展,使其不仅限于boolean
类型)。useUrlState
:管理当前url,包含添加参数,修改参数、删除参数(将该参数值设为undefined
)等。useCookieState
:对Cookies
进行管理,包含过期时间、cookies
设置等。useLocalStorageState
&useSessionStorageState
:管理localStorage
和sessionStorage
。useRafState
:只在requestAnimationFrame
回调时更新state
,常用于性能优化。useSafeState
:与useState
用法一致,可以避免组件卸载后更新状态而导致的内存泄漏。useGetState
:给useState
增加了一个getter
方法,以获取当前最新值(基于useRef
实现)。
其他:
useBoolean
:返回的数组第二个参数为三个方法,setTrue
设为true
,setFalse
设为false
,toggle
取反。useMap
:对Map
类型状态管理。提供对Map
类型值的增删改查等操作。useSet
:对Set
类型对象状态管理。usePrevious
:对上一次的值/状态进行保存。useDebounce
&useThrottle
:对值进行防抖和节流处理。
五、执行副作用操作-Effect hooks
常用:
useUpdateEffect
:用法同useEffect
,但会忽略首次执行,仅在依赖更新时执行。useUpdateLayoutEffect
:用法同useLayoutEffect
,仅依赖更新时执行。useAsyncEffect
:支持异步函数的useEffect
。useAsyncEffect(async()=>{},[])
useDeepCompareEffect
&useDeepCompareLayoutEffect
:深比较(基于lodash/isEqual
)useEffect
和useLayoutEffect
第二参数deps
是否变化。useInterval
&useTimeout
:基于useRef
管理setInterval
和setTimeout
,卸载时自带销毁定时器。useRafInterval
&useRafTimeout
:基于requestAnimationFrame
实现定时器。useUpdate
:返回update
函数,调用该函数会强制组件重新渲染。
其他:
useDebounceEffect
&useThrottleEffect
:给useEffect
增加防抖/节流能力。useDebounceFn
&useThrottleFn
:用来处理防抖/节流函数的hooks。useLockFn
:异步函数增加竞态锁,防止并发执行(函数执行完成前,其余的异步操作都会被忽略)。
六、副作用DOM相关操作
事件监听:
useEventListener
:添加监听事件,包含键盘操作监听。useKeyPress
:监听键盘按键,支持组合键、按键别名。useClickAway
:监听除了目标元素外的点击事件,支持传入多个目标元素。useDocumentVisibility
:监听页面是否可见(最小化)。useEventTarget
:对表单控件内onChange
和value
逻辑的封装。useExternal
:引入外部脚本,js
或css
资源。useLongPress
:监听目标元素长按事件。useMouse
:监听鼠标位置。useResponsive
:监听浏览器窗口响应式信息(窗口大小改变)。useScroll
:监听元素的滚动位置。useFocusWithin
:监听当前焦点是否在某个区域内。
DOM相关:
useDrop
&useDrag
:useDrop
单独使用可接收文件、文字和网址拖拽;useDrag
允许DOM节点被拖拽,需配合useDrop
使用。useTitle
&useFavicon
:修改标签页title
&favicon
。useFullscreen
:相当于F11
的全屏。useHover
:监听dom
元素是否有鼠标悬停。useMutationObserver
:监听指定dom
树发生变化的hooks。useInViewport
:监听指定dom
元素是否在可视区域内。useSize
:监听dom
节点尺寸变化的hooks。
七、优化相关hooks
useControllableValue
:管理组件内部状态是否受父组件控制。useCreation
:可替代useMemo
和useRef
,对其性能隐患进行了优化。useEventEmitter
:可跨组件事件通知,共享事件。useIsomorphicLayoutEffect
:ssr模式下,替换useLayoutEffect
,可避免出现警告。useLatest
:返回当前最新hooks,避免闭包问题。useMemoizedFn
:替代useCallback
,不需要deps
参数,且函数地址不会变化。useReactive
:可替换useState
,不需要setState
,直接修改state
即可。
debug:
useTrackedEffect
:可追踪是哪个依赖出发了useEffect
的执行。useWhyDidYouUpdate
:用来排查是哪个属性导致了组件的rerender
。