react16路由缓存react-activation详解

react-activation路由缓存详解
本文详细介绍了如何在React项目中使用TS实现路由缓存,包括配置ReactRouter、React-Activation库以及解决手动控制缓存、滚动位置保存等问题,提供了完整代码实例。
该文章已生成可运行项目,

夸克资源分享:
表情包:https://pan.quark.cn/s/5b9ddeb237fe
工具箱:https://pan.quark.cn/s/aa2d6a730482,图吧、美蛋、路遥、入梦等
Fiddler Everywhere抓包:https://pan.quark.cn/s/6b1e2fbae019
Adobe:https://pan.quark.cn/s/13e39cfeaadb,先看安装教程
JetBranis开发者工具:https://pan.quark.cn/s/16e94dcff1f7,先看安装教程下的jetbra教程
逆向工具:https://pan.quark.cn/s/50e93c8ca54c
前端项目搭建集锦:https://blog.youkuaiyun.com/randy521520/article/details/146998467

react PC端项目构建TS,react@18.2.0+antd+vite+axios+redux+sass+ts 完整版代码下载:
https://download.youkuaiyun.com/download/randy521520/88922625
react PC端项目构建,react@18.2.0+antd+vite+axios+redux+sass完整版代码下载:
https://download.youkuaiyun.com/download/randy521520/88922569
react移动端项目构建TS,react@18.2.0+react-vant+vite+axios+redux+sass+ts完整版代码下载:
https://download.youkuaiyun.com/download/randy521520/88917557
react移动端项目构建,react@18.2.0+react-vant+vite+axios+redux+sass完整版代码下载:
https://download.youkuaiyun.com/download/randy521520/88917543

一、简介

React 路由缓存是指在使用 React Router 或其他路由管理库时,通过一些技术手段来缓存已经加载的页面组件,以便在用户再次访问这些页面时能够更快地呈现内容,提升用户体验和性能。
在这里插入图片描述

二、配置路由缓存

1.router.jsx,创建路由

import {Suspense, lazy} from "react";
import {Navigate, useRoutes} from "react-router-dom";
import KeepAlive from "react-activation";

const routes = [{
    path: '/',
    element: <Navigate to="/home" replace/>,
}, {
    id: "home",
    path: '/home',
    component: lazy(() => import('@pages/home/home.jsx'))
}, {
    id: "list",
    path: '/list',
    component: lazy(() => import('@pages/list/list.jsx'))
}];

const generateRouter = (routers) => {
    return routers.map((item) => {
        if (item.children) {
            item.children = generateRouter(item.children)
        }
        item.element = <KeepAlive cacheKey={item.path} id={item.path} name={item.path}>
            <Suspense>
                {item.component ? <item.component/> : item.element}
            </Suspense>
        </KeepAlive>;
        return item
    })
};

const RootRouter = () => useRoutes(generateRouter(routes));

export default RootRouter;

2.App.jsx,使用路由

import RootRouter from "@/router.jsx";

function App() {
    return (
        <>
            <RootRouter/>
        </>
    )
}

export default App

3.main.js,使用路由缓存

import ReactDOM from "react-dom/client";
import {BrowserRouter} from "react-router-dom";
import App from "./App.jsx";
import {AliveScope} from "react-activation";

ReactDOM.createRoot(document.getElementById('root')).render(
  <BrowserRouter>
      <AliveScope>
          <App />
      </AliveScope>
  </BrowserRouter>,
)
三、路由缓存生命周期

1.useActivate(()=>{}):组件激活,第一次进入组件,不会进入该生命周期,因为组件未缓存
2.useUnactivate(()=>{}): 组件离开,缓存组件
3.案例,class组件可使用componentDidActivate 与 componentWillUnactivate

import {useActivate, useUnactivate} from "react-activation";
import {useState} from "react";
import {useNavigate} from "react-router-dom";

const Home = () => {
    const navigate = useNavigate();
    let [count, setCount] = useState(0);
    useActivate(() => {
        console.log('组件已激活')
    })
    useUnactivate(() => {
        console.log('组件已缓存')
    })
    const onCountChange = ()=>{
        count++;
        console.log(`count值:${count}`)
        setCount(count)
    }

    const goRouter = ()=>{
        navigate('/list')
    }

    return (<>
        <button onClick={onCountChange}>count改变</button>
        <button onClick={goRouter}>跳转list</button>
        <div>count值:{count}</div>
    </>)
}

export default Home;

在这里插入图片描述在这里插入图片描述

四、手动控制缓存

1.const { drop, dropScope, refresh,refreshScope,clear, getCachingNodes } = useAliveController()

  • drop(name):卸载缓存,需给<KeepAlive>加上name,如果没有缓存但是需要清除缓存状态需使用refresh(name)。仅卸载命中 <KeepAlive> 的第一层内容,不会卸载 <KeepAlive> 中嵌套的、未命中的<KeepAlive>
  • dropScope(name):卸载缓存,,需给<KeepAlive>加上name,如果没有缓存但是需要清除缓存状态需使用refresh(name)。将卸载命中 <KeepAlive> 的所有内容,包括 <KeepAlive> 中嵌套的所有 <KeepAlive>
  • refresh(name):刷新缓存,仅刷新命中 <KeepAlive> 的第一层内容,不会刷新 <KeepAlive> 中嵌套的、未命中的 <KeepAlive>
  • refreshScope(name):刷新缓存,将刷新命中<KeepAlive> 的所有内容,包括 <KeepAlive> 中嵌套的所有 <KeepAlive>
  • clear():将清空所有缓存中的 KeepAlive
  • getCachingNodes():获取所有缓存中的节点
    2.class组件需使用withAliveScope装饰器
@withAliveScope
class App extends Component {
  render() {
    const { drop, dropScope, clear, getCachingNodes } = this.props

    return (
      ...
    )
  }
}
五、自动缓存

给需要控制缓存的 <KeepAlive /> 标签增加 when 属性
1.当 when 类型为 Boolean 时

  • true: 卸载时缓存
  • false: 卸载时不缓存
<KeepAlive when={true}>

2.当 when 类型为 Array 时

  • 第 1 位参数表示是否需要在卸载时缓存
  • 第 2 位参数表示是否卸载 <KeepAlive> 的所有缓存内容,包括 <KeepAlive> 中嵌套的所有 <KeepAlive>
<KeepAlive when={[false, true]}>
   <KeepAlive>...</KeepAlive>
</KeepAlive>

3.当 when 类型为 Function 时,可返回Boolean或 Array

<KeepAlive when={() => true}>
<KeepAlive when={() => [false, true]}>
六、多份缓存,用于动态参数的路由

1./item 路由会按 id 来做不同呈现,但只能保留同一份缓存

<Route
path="/item/:id"
  render={props => (
    <KeepAlive>
      <Item {...props} />
    </KeepAlive>
  )}
/>

2.解决方法,给KeepAlive增加id

<Route
  path="/item/:id"
  render={props => (
    <KeepAlive id={props.match.params.id}>
      <Item {...props} />
    </KeepAlive>
  )}
/>
七、保存滚动位置

1.KeepAlive组件增加saveScrollPosition=参数

<KeepAlive saveScrollPosition={true} />

2.如果组件共享了屏幕滚动容器如 document.body 或 document.documentElement, 将 saveScrollPosition 属性设置为 “screen”

<KeepAlive saveScrollPosition="screen" />
八、使用react-activation存在的问题

1.<KeepAlive /> 中需要有一个将 children 传递到 <AliveScope /> 的动作,故真实内容的渲染会相较于正常情况慢一拍,将会对严格依赖生命周期顺序的功能造成一定影响,获取组件ref时可能会获取不到,如下面代码:刷新页面后useEffect中并未拿到Test的ref

import KeepAlive, {useActivate, useUnactivate} from "react-activation";
import {forwardRef, useEffect, useImperativeHandle, useRef, useState} from "react";
import {useNavigate} from "react-router-dom";

const _Test = (props,ref)=>{
    useImperativeHandle(ref,()=>({
        onClick:()=>{
            console.log(1)
        }
    }))
    return <>test组件</>
};
const Test = forwardRef(_Test);

const Home = () => {
    const navigate = useNavigate();
    let [count, setCount] = useState(0);
    let testRef = useRef();
    useEffect(() => {
        console.log(testRef.current)
    }, []);

    useActivate(() => {
        console.log('组件已激活')
    })
    useUnactivate(() => {
        console.log('组件已缓存')
    })
    const onCountChange = ()=>{
        count++;
        console.log(`count值:${count}`)
        setCount(count)
    }

    const goRouter = ()=>{
        navigate('/list')
    }

    return (<>
        <button onClick={onCountChange}>count改变</button>
        <button onClick={goRouter}>跳转list</button>
        <div>count值:{count}</div>
        <KeepAlive>
            <Test ref={testRef}/>
        </KeepAlive>
    </>)
}

export default Home;

在这里插入图片描述

  • 函数组件解决办法使用定时器延时获取ref
import KeepAlive, {useActivate, useUnactivate} from "react-activation";
import {forwardRef, useEffect, useImperativeHandle, useRef, useState} from "react";
import {useNavigate} from "react-router-dom";

const _Test = (props,ref)=>{
    useImperativeHandle(ref,()=>({
        onClick:()=>{
            console.log(1)
        }
    }))
    return <>test组件</>
};
const Test = forwardRef(_Test);

const Home = () => {
    const navigate = useNavigate();
    let [count, setCount] = useState(0);
    let testRef = useRef();
    useEffect(() => {
        setTimeout(()=>{
            console.log(testRef.current)
        },30)
    }, []);

    useActivate(() => {
        console.log('组件已激活')
    })
    useUnactivate(() => {
        console.log('组件已缓存')
    })
    const onCountChange = ()=>{
        count++;
        console.log(`count值:${count}`)
        setCount(count)
    }

    const goRouter = ()=>{
        navigate('/list')
    }

    return (<>
        <button onClick={onCountChange}>count改变</button>
        <button onClick={goRouter}>跳转list</button>
        <div>count值:{count}</div>
        <KeepAlive>
            <Test ref={testRef}/>
        </KeepAlive>
    </>)
}

export default Home;

在这里插入图片描述

  • class组件解决方案可采用@withActivation装饰器
@withActivation
class Test extends Component {
  componentDidMount() {
    console.log(this.outside) // will log <div /> instance
    console.log(this.inside) // will log <div /> instance
  }

  render() {
    return (
      <div>
        <div
          ref={ref => {
            this.outside = ref
          }}
        >
          Outside KeepAlive
        </div>
        <KeepAlive>
          <div
            ref={ref => {
              this.inside = ref
            }}
          >
            Inside KeepAlive
          </div>
        </KeepAlive>
      </div>
    )
  }
}

2.官方还介绍到对 Context 的破坏性影响、对依赖于 React 层级的功能造成影响,我使用的react版本是V18.2.0、react-activation版本是V0.12.4,暂时没遇到,有遇到的伙伴可查看git文档:https://github.com/CJY0208/react-activation/blob/HEAD/README_CN.md

本文章已经生成可运行项目
### React 18 中使用 `react-activation` 实现路由缓存React 18 中,可以借助 `react-activation` 来实现路由跳转返回时保留组件的状态。以下是具体实现方法以及需要注意的关键点。 #### 安装依赖 首先需要安装 `react-activation` 和 `react-router-dom` 的最新版本: ```bash yarn add react-activation react-router-dom@6 # 或者 npm install react-activation react-router-dom@6 ``` #### 配置入口文件 为了支持路由缓存功能,在应用的根节点需要用 `<AliveScope>` 组件包裹整个应用程序实例。这一步非常重要,因为它是 `react-activation` 提供的核心机制之一[^1]。 ```javascript // index.js import React from 'react'; import ReactDOM from 'react-dom/client'; import { BrowserRouter as Router } from 'react-router-dom'; import { AliveScope } from 'react-activation'; import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <Router> <AliveScope> <App /> </AliveScope> </Router> ); ``` 在这里,我们通过 `ReactDOM.createRoot()` 方法创建了一个新的 Root 渲染器,并将其用于渲染包含 `<AliveScope>` 的树结构[^2]。 #### 路由配置 接下来定义路由表,并针对需要缓存状态的组件使用 `<KeepAlive>` 进行包裹。如果多个组件共享相同的 ID,则它们会被视为同一个缓存单元;因此可以通过设置不同的 `id` 属性来区分不同组件之间的独立缓存。 ```javascript // router.js import React from 'react'; import { Routes, Route } from 'react-router-dom'; import Home from './pages/Home'; import Detail from './pages/Detail'; import List from './pages/List'; import Login from './pages/Login'; import { KeepAlive } from 'react-activation'; const RouterConfig = () => { return ( <Routes> <Route path="/" element={<Home />} /> <Route path="/list" element={ <KeepAlive id="List"> <List /> </KeepAlive> } /> <Route path="/detail/:id" element={ <KeepAlive id="Detail"> <Detail /> </KeepAlive> } /> <Route path="/login" element={<Login />} /> </Routes> ); }; export default RouterConfig; ``` 在这个例子中,我们将 `/list` 和 `/detail/:id` 页面分别用 `<KeepAlive>` 包裹起来,从而实现了这两个页面在切换前后能够保存其内部状态而不被重新加载。 #### 注意事项 当使用 `react-activation` 结合 React 18 及以上版本开发项目时,请注意以下几点: - **StrictMode 支持**:React 18 引入了更严格的模式检测逻辑,默认情况下会对某些生命周期钩子执行两次调用来帮助开发者发现潜在问题。然而这种行为可能会影响部分插件的表现形式,所以建议测试阶段关闭 Strict Mode 测试兼容性后再决定是否开启它[^2]。 - **Concurrent Rendering (并发渲染)**:由于 React 18 开始全面推广 Suspense API 并优化异步更新策略,可能会对传统同步阻塞型操作造成一定干扰。尽管目前 `react-activation` 已经适配大部分场景下的正常运行环境,但在极端条件下仍需额外验证是否存在异常情况发生。 --- ### 示例代码总结 完整的示例代码如下所示: ```javascript // index.js import React from 'react'; import ReactDOM from 'react-dom/client'; import { BrowserRouter as Router } from 'react-router-dom'; import { AliveScope } from 'react-activation'; import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <Router> <AliveScope> <App /> </AliveScope> </Router> ); // App.js import React from 'react'; import RouterConfig from './router'; function App() { return <RouterConfig />; } export default App; // router.js import React from 'react'; import { Routes, Route } from 'react-router-dom'; import Home from './pages/Home'; import Detail from './pages/Detail'; import List from './pages/List'; import Login from './pages/Login'; import { KeepAlive } from 'react-activation'; const RouterConfig = () => { return ( <Routes> <Route path="/" element={<Home />} /> <Route path="/list" element={ <KeepAlive id="List"> <List /> </KeepAlive> } /> <Route path="/detail/:id" element={ <KeepAlive id="Detail"> <Detail /> </KeepAlive> } /> <Route path="/login" element={<Login />} /> </Routes> ); }; export default RouterConfig; ``` ---
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

局外人LZ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值