一、基于react-route6 useOutlet实现
二、代码呈现
import React, { useRef, createContext, useContext } from 'react'
import { useOutlet, useLocation, matchPath } from 'react-router-dom'
import type { FC } from 'react'
//在组件外部建立一个Context
export const KeepAliveContext = createContext<KeepAliveLayoutProps>({ keepalive: [], keepElements: {} })
//给予页面缓存设置条件判断
const isKeepPath = (aliveList: any[], path: string) => {
let isKeep = false
aliveList.map(item => {
if (item === path) {
isKeep = true
}
if (item instanceof RegExp && item.test(path)) {
isKeep = true
}
})
return isKeep
}
//判断当前页面是否已缓存,是则控制hidden开关显示 ,不是则正常渲染
export function useKeepOutlets() {
const location = useLocation()
const element = useOutlet()
const { keepElements, keepalive } = useContext<any>(KeepAliveContext)
const isKeep = isKeepPath(keepalive, location.pathname)
if (isKeep) {
keepElements.current[location.pathname] = element
}
//标签的显示与隐藏
return <> {
Object.entries(keepElements.current).map(([pathname, element]: any) => (
<div key={pathname}
style={{ height: '100%', width: '100%', position: 'relative', overflow: 'hidden auto' }} className="rumtime-keep-alive-layout"
hidden={!matchPath(location.pathname, pathname)}>
{element}
</div>
))
}
<div hidden={isKeep} style={{ height: '100%', width: '100%', position: 'relative', overflow: 'hidden auto' }} className="rumtime-keep-alive-layout-no">
{!isKeep && element}
</div>
</>
}
//设置公共组件类型
interface KeepAliveLayoutProps {
keepalive: any[]
keepElements?: any
dropByCacheKey?: (path: string) => void
}
//封装公共组件
const KeepAliveLayout: FC<KeepAliveLayoutProps> = (props) => {
const { keepalive, ...other } = props
const keepElements = React.useRef<any>({})
function dropByCacheKey(path: string) {
keepElements.current[path] = null
} return (<KeepAliveContext.Provider
value={{ keepalive, keepElements, dropByCacheKey }}
{...other} />)
}
export default KeepAliveLayout
代码分析
isKeepPath
配置 keepalive 支持字符串和正则,通过它来判断,当前页面是否需要状态保持,因为如果整个项目的页面都保持状态的话,对性能是很大的消耗
参数1为可缓存路径或正则表达式组成的数组,参数2为当前路径。
若当前路径在已缓存路径数组中或其路径符合正则表达式则isKeep为true,反之为false
useKeepOutlets
通过判断当前页面是否是需要保持的页面来对页面 DOM 做一个 hidden
显隐开关。
需要注意的是所有被指定状态保持的页面在首次渲染之后,都会被挂载在页面 DOM 树上,仅仅是使用 !matchPath(location.pathname, pathname)
控制显隐。
而没有被指定状态保持的页面,则是使用 {!isKeep && element}
控制,走 React 组件正常的生命周期。
location
当前路径信息
element
获取当前路由组件即当前配置下的嵌套路由组件
useContext<any>(KeepAliveContext)
通过useContext()钩子函数获取Context对象中的属性,已便于组件之间共享状态
isKeep
将当前路径利用页面缓存设置条件判断是否为已缓存路径,若符合条件,isKeep为true,则将keepElements Ref中以当前组件路径名为属性名的属性绑定当前路由组件
Object.entries
描述:Object.entries()
返回一个数组,其元素是与直接在object
上找到的可枚举属性键值对相对应的数组。属性的顺序与通过手动循环对象的属性值所给出的顺序相同。
可枚举:枚举的功能类似于字面量类型+联合类型组合的功能,也可以表示一组明确的可选值
参数:可以返回其可枚举属性的键值对的对象
返回值:给定对象自身可枚举属性的键值对数组
key
保持 key 不变,就不会触发 React 的重绘
hidden
控制显示与隐藏开关
matchPath(location.pathname, pathname)}
所有被指定状态保持的页面在首次渲染之后,都会被挂载在页面 DOM 树上,仅仅是使用!matchPath(location.pathname, pathname)
控制显示隐藏。
//matchPath:参数1为当前路径,参数2为缓存路径,确定当前路由路径是否与缓存路径匹配
而没有被指定状态保持的页面,则是使用 {!isKeep && element}
控制,走 React 组件正常的生命周期
KeepAliveLayout
为封装后暴露的组件
FC
React.FC
是函数式组件,是在TypeScript下使用的一个泛型,全称为React.FunctionComponent
,React.FC<>
可检测指定属性类型
keepElements
使用 React.useRef<any>({})
来做页面数据保存的节点,是因为我们的上下文不被重新渲染的话 keepElements
就不会被重置,相当于 key
dropByCacheKey
dropByCacheKey为清除缓存的函数,通过控制当前组件的ref来销毁组件
other
const { keepalive, ...other } = props
中...other为其他配置,这里我们直接遍历继承即可
Provider
(<KeepAliveContext.Provider value={{ keepalive, keepElements, dropByCacheKey }} {...other} />)
原理:
每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化。
Provider 接收一个 value
属性,传递给消费组件。一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。
当 Provider 的 value
值发生变化时,它内部的所有消费组件都会重新渲染
三、使用
import KeepAliveLayout, { useKeepOutlets, KeepAliveContext }from'@/components/KeepAliveLayout'
import { useLocation } from 'react-router-dom'
import React, { useState, useContext } from 'react'
// 使用KeepAliveLayout中的useKeepOutlets获取当前渲染的页面内容
const Layout = () => {
const element = useKeepOutlets()
return (
{element}
)
}
// 使用 KeepAliveLayout 包裹上下文
const App = () => {
return (
<KeepAliveLayout keepalive={[/./]}>//不可能组件都缓存吧所以需要设置缓存条件,可传可缓存的路径或正则表达式
// App
</KeepAliveLayout>
);
}
// 使用 useContext 获取 dropByCacheKey 清除缓存
const Home = () => {
const { dropByCacheKey } = useContext<any>(KeepAliveContext);
const { pathname } = useLocation();
return (
<button onClick={() => dropByCacheKey(pathname)}>清除缓存</button>
)
}
到此这篇关于React-Route6实现keep-alive效果的文章就介绍到这了,更多相关React-Route6 keep-alive 内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!