文章目录

  • 一、KeepAlive 组件
  • 二、AliveScope 容器
  • 三、useAliveController Hook
  • 四、生命周期
  • 五、完整示例

react-activation 主要解决 React 项目中的「页面缓存」需求(是第三方库,非React 官方),类似于 Vue 中的 <KeepAlive>

功能说明
<KeepAlive> 组件缓存组件 DOM,不卸载
<AliveScope> 容器缓存容器,必须包裹在外层
useAliveController Hook提供缓存管理 API(如 droprefresh
useActivate / useUnactivate 生命周期激活与失活钩子
  • 地址:GitHub 仓库

  • NPM:https://www.npmjs.com/package/react-activation

  • 适配版本:推荐用于 React 16 和 React 17(React 18 存在一些副作用不稳定问题)


一、KeepAlive 组件

<KeepAlive> 是一个高阶组件,用于缓存(keep-alive)React 组件的状态,类似于 Vue 的 ,在组件卸载后保留其状态和 DOM,提升体验(例如表单不丢失、滚动位置保留等)。

用于缓存组件的 UI 和状态,当 <AdminHome /> 页面切走再回来,状态不会丢失,组件不会重新挂载。

<KeepAlive id="admin-home"><AdminHome />
</KeepAlive>

基本属性

属性类型默认值说明
idstring-缓存唯一标识,必须唯一,一般用 pathname
namestringid可选的缓存名称(某些缓存操作可以用 name)
whenbooleantrue是否启用缓存,设为 false 表示不缓存
saveScrollPositionfalse | 'screen' | 'container' | ((node: HTMLElement) => any)false是否保存并恢复页面的滚动位置:
false(默认):不保存滚动位置
'screen': 保存并恢复页面(window)滚动位置
'container': 自动寻找最近的滚动容器保存滚动位置
(node) => HTMLElement: 自定义返回要记录滚动位置的 DOM 元素
autoFreezebooleantrue切换时是否冻结 DOM,节省资源
extraanyundefined附加信息,可在缓存控制器中使用(不常用)

二、AliveScope 容器

<AliveScope>react-activation 提供的作用域容器,用来管理缓存组件的上下文和分组控制。

  • 提供上下文,让 KeepAlive 可以记录并复用组件状态

  • 管理组件缓存生命周期

  • 可用于分组销毁缓存(配合 dropScope(scopeId))

  1. <AliveScope> 是必须包裹住所有 <KeepAlive> 的组件,否则 KeepAlive 不会起作用。

    如果不包裹 <KeepAlive>,它内部就无法访问缓存管理上下文:

    • KeepAlive 会直接按普通组件渲染,不会缓存
    • useActivate / useUnactivate 钩子不会被调用
    • useAliveController() 获取到的控制器是空的
    <AliveScope name="user-scope"><KeepAlive id="user-list"><UserList /></KeepAlive><KeepAlive id="user-detail"><UserDetail /></KeepAlive>
    </AliveScope>
    

    标准写法是直接把 <AliveScope> 放在根节点。

    import React from 'react';
    import ReactDOM from 'react-dom';
    import { AliveScope } from 'react-activation';
    import App from './App';ReactDOM.render(<AliveScope><App /></AliveScope>,document.getElementById('root')
    );
    
  2. AliveScope 可选属性

    属性类型默认值说明
    namestringdefault作用域唯一标识,可用于区分多个 AliveScope
    includestring[][]允许缓存的组件 ID 列表(白名单)
    excludestring[][]禁止缓存的组件 ID 列表(黑名单)
    maxnumberInfinity缓存数量上限,超过后会自动淘汰最久未使用的(LRU 策略)
    cacheKeystringnamelocalStorage 缓存相关的 key(需要搭配 persist
    persistbooleanfalse是否持久化缓存(刷新页面或关闭浏览器后,再次进入,缓存状态仍然保留,保存在 localStorage)
    autoClearbooleanfalse是否在页面刷新后自动清空缓存(防止缓存穿透,防止过时数据)
  3. 配合 KeepAlive 使用

    import { AliveScope, KeepAlive } from 'react-activation';
    import UserList from './UserList';
    import UserDetail from './UserDetail';export default function Root() {return (<AliveScope name="user-scope" include={['user-list']}><KeepAlive id="user-list"><UserList /></KeepAlive><KeepAlive id="user-detail"><UserDetail /></KeepAlive></AliveScope>);
    }
    
    • 只有 user-list 会被缓存(受 include 控制)

    • 可以通过 useAliveController().dropScope('user-scope') 一键清除

  4. 多个 AliveScope 的使用方式

    把用户模块和管理员模块的缓存完全隔离开,这样每个作用域有自己独立的缓存池

    <!-- 用户模块 -->
    <AliveScope name="user-scope"><KeepAlive id="user-list"><UserList /></KeepAlive><KeepAlive id="user-detail"><UserDetail /></KeepAlive>
    </AliveScope><!-- 管理员模块 -->
    <AliveScope name="admin-scope"><KeepAlive id="admin-home"><AdminHome /></KeepAlive>
    </AliveScope>
    

    这时可以使用 useAliveController() 来获取缓存控制器,跨作用域控制:

    import { useAliveController } from 'react-activation';export default function ClearButton() {const { dropScope } = useAliveController();return (<><button onClick={() => dropScope('user-scope')}>清空用户模块缓存</button><button onClick={() => dropScope('admin-scope')}>清空管理员模块缓存</button></>);
    }
    
    • dropScope(‘user-scope’) 会销毁 user-scope 作用域中所有 KeepAlive 缓存

    • 也可以用 refreshScope(name) 强制刷新一个作用域内所有组件


三、useAliveController Hook

这是一个自定义 Hook,提供对缓存组件的控制,比如手动刷新(drop)某个缓存组件、获取缓存状态等。

const { drop, dropScope, refresh } = useAliveController();// 单作用域: 卸载某个缓存组件(通过 KeepAlive 的 id)
// 使用场景:点击“关闭标签页”
drop('user-list');  // 卸载 id 为 'user-list' 的 KeepAlive 缓存
drop('user-detail');
// 👉 全部要写一遍,或维护复杂缓存 id 列表// 多作用域: 卸载该作用域下的所有缓存组件(通过 AliveScope 的 name),比 drop(id) 更高级别的操作
// 使用场景:退出登录清除所有缓存
dropScope('user-scope'); // 卸载 <AliveScope name="user-scope"> 下的所有 KeepAlive 缓存// 强制刷新(先卸载再重建)
// 使用场景:点击“重置表单”或“刷新页面”
refresh('user-list'); // 会先 drop(‘user-list’),然后立刻重新挂载组件
  • dropScope 的参数是 中的 name。

  • 使用前确保这些组件确实是包裹在 <AliveScope> 内的。

  • AliveScope 是 react-activation 中用于分组缓存的容器,必须明确设置 name 才能使用 dropScope。
    .

🔥 在 react-activation 中,组件必须处于「非激活状态」( 即 KeepAlive 的 when 为 false、或组件被隐藏 ),才允许卸载或刷新。不会立即卸载当前正在显示的组件

方法作用会立即卸载当前正在显示的组件吗?何时真正卸载?
drop(id)删除某个缓存组件的状态❌ 不会立即卸载当前正在显示的⚠️ 当该组件被切换隐藏时
dropScope(scopeId)删除整个 AliveScope 中的缓存❌ 不会立即卸载当前正在显示的⚠️ 当前组件不显示后才会销毁
refresh(id)删除后重新创建组件❌ 不会立即刷新当前激活组件⚠️ 必须切到其他组件再切回来才生效
  • drop / dropScope / refresh 不会卸载当前正在显示的组件

  • 它们只对非激活(未渲染)的组件生效

  • ✅ 正确的做法是:切换走 → drop → 才生效:

    history.push('/other');
    await drop('/current'); // ✅ 现在它处于非激活状态,drop 生效
    

四、生命周期

react-activation(React 第三方库) 提供的自定义 Hook,模拟 Vue 的 activated / deactivated 生命周期。

<AliveScope><KeepAlive id="user"><UserPage /></KeepAlive>
</AliveScope>import { useActivate, useUnactivate, useAliveEffect } from 'react-activation';// 组件激活时调用(进入或返回该缓存组件时),替代 useEffect 的 didShow
useActivate(() => {console.log('页面被激活(显示): 进入时刷新数据')
});// 组件失活时调用(从该组件跳出,但未卸载),类似 componentWillPause
useUnactivate(() => {console.log('页面被隐藏但未卸载: 退出时保存状态')
});// 只有当组件是“激活状态”时,才会执行 useEffect,可以响应 deps 的变化,可替代 useEffect + useActivate 组合
useAliveEffect(() => {const timer = setInterval(() => {console.log('只在激活状态时轮询');}, 1000);return () => clearInterval(timer);
}, []);

类似于 Vue3:

<template><KeepAlive include="UserPage"><component :is="currentView" /></KeepAlive>
</template>// 原生生命周期钩子
onActivated(() => {console.log('组件被缓存激活');
});onDeactivated(() => {console.log('组件被缓存关闭');
});

五、完整示例

✅ 标签切换自动缓存
✅ 点击关闭标签页 → 销毁对应缓存
✅ 支持多个 AliveScope 管理模块分组
✅ 使用 KeepAlive + useActivate + useUnactivate

  1. main.tsx(注册多个作用域)

    import React from 'react';
    import ReactDOM from 'react-dom';
    import { AliveScope } from 'react-activation';
    import App from './App';ReactDOM.render(<><AliveScope name="module-user"><App /></AliveScope>{/* 可拓展其他模块作用域 */}</>,document.getElementById('root')
    );
    
  2. App.tsx(入口,渲染标签页)

    import React from 'react';
    import TabView from './components/TabView';export default function App() {return (<div><TabView /></div>);
    }
    
  3. TabView.tsx(核心组件)

    import React, { useState } from 'react';
    import { KeepAlive, useAliveController } from 'react-activation';
    import PageA from './PageA';
    import PageB from './PageB';
    import PageC from './PageC';const tabComponents: Record<string, React.ReactNode> = {A: <PageA />,B: <PageB />,C: <PageC />,
    };const TabView = () => {const [tabs, setTabs] = useState(['A', 'B', 'C']);const [active, setActive] = useState('A');const { drop } = useAliveController();const closeTab = async (key: string) => {// 比如当前 tabs 是 ['A', 'B', 'C'],要关闭 A 标签setTabs(prev => prev.filter(t => t !== key)); // 更新标签页列表(异步),由['A', 'B', 'C'] -> ['B', 'C']if (active === key) { // 如果关闭的是当前激活标签const other = tabs.find(t => t !== key); // 从标签页列表['A', 'B', 'C']中找出第一个非 key 的 tab(即 'B')if (other) setActive(other); // 激活新标签B}await drop(`page-${key}`); // 卸载对应标签的缓存组件(卸载'page-A')};return (<div><div style={{ display: 'flex', gap: 8, marginBottom: 12 }}>{tabs.map(tab => (<divkey={tab}style={{padding: '6px 12px',border: active === tab ? '2px solid blue' : '1px solid #ccc',borderRadius: 4,cursor: 'pointer',background: '#f7f7f7',position: 'relative',}}onClick={() => setActive(tab)}>Page {tab}<spanonClick={e => {e.stopPropagation();closeTab(tab);}}style={{marginLeft: 6,color: 'red',cursor: 'pointer',fontWeight: 'bold',}}>×</span></div>))}</div><div style={{ border: '1px solid #ccc', padding: 12 }}>{tabs.map(tab =>active === tab ? (<KeepAlive id={`page-${tab}`} key={tab}>{tabComponents[tab]}</KeepAlive>) : null)}</div></div>);
    };export default TabView;
    
  4. PageA.tsx(缓存与生命周期)

    import React, { useState } from 'react';
    import { useActivate, useUnactivate } from 'react-activation';export default function PageA() {const [count, setCount] = useState(0);useActivate(() => {console.log('PageA 激活');});useUnactivate(() => {console.log('PageA 失活');});return (<div><h2>Page A</h2><p>计数: {count}</p><button onClick={() => setCount(c => c + 1)}>+1</button></div>);
    }
    

    PageB.tsx、PageC.tsx 同上

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/85315.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/85315.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/85315.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CentOS 7内核升级方案

关于升级 CentOS 7 系统内核至 4.19 版本的可执行升级方案,可根据实际情况进行调整和完善,希望能对大家有所帮助: 一、升级背景与目的 随着业务的发展和系统稳定性的要求,当前 CentOS 7 系统所使用的内核版本 3.10.0-1160.el7.x86_64 已经无法满足部分新功能需求以及面临…

树莓派实验实践记录与技术分析

一、内核驱动开发&#xff1a;hello 模块实现 驱动程序代码 #include <linux/init.h> #include <linux/module.h> static int __init hello_init(void) { printk(KERN_INFO "hello kernel\n"); return 0; } module_init(hello_init); static void …

【秦九绍算法】小红的 gcd

题目 牛客网&#xff1a;小红的 gcd 题目分析 我们知道&#xff0c;求gcd就用欧几里得算法&#xff08;辗转相除法&#xff09;&#xff1a;gcd(a,b)gcd(b,a mod b)。但是这题的a非常大&#xff0c;最大是一个1e6位数&#xff0c;无法使用任何数据类型存储。如果使用高精度…

AWS服务监控之EC2内存监控

首先在IAM里找到角色&#xff0c;创建角色&#xff0c;选择EC2 然后在被监控的机器上安装cloudwatch-agent 官方链接在本地服务器上安装 CloudWatch 代理 - Amazon CloudWatch wget https://s3.amazonaws.com/amazoncloudwatch-agent/redhat/amd64/latest/amazon-cloudwatch-a…

鸿蒙 ArkWeb 和 H5混编开发

ArkWeb Web 相关标准技术(HTML/CSS/JS)&#xff0c;是业内支持性最广泛的技术&#xff0c;可以在最广泛的平台下实现“一次编写到处运行”&#xff1b;大部分对性能无需极致要求的应用页面&#xff0c;都可以使用 Web 技术来实现。 鸿蒙 ArkWeb Kit&#xff08;方舟 Web&…

设计模式-迪米特法则(Law of Demeter, LoD)

迪米特法则&#xff08;Law of Demeter, LoD&#xff09; 别名&#xff1a;最少知识原则&#xff08;Least Knowledge Principle&#xff09; 核心思想&#xff1a;一个对象应尽可能少地与其他对象发生交互&#xff0c;只与直接的朋友&#xff08;成员变量、方法参数、方法返回…

python获取AB直线间任意一点经纬度

获取AB直线间任意一点经纬度 1、目标 已知A点经纬度,距离;B点经纬度,距离,如果C点在AB之间,且知道C点距离,求C点的经纬度信息。 目标:在AB这条直线上,根据给定的距离(从A点开始沿直线到某点的距离)来求该点的经纬度。 2、方法 首先计算AB的总长度(大圆距离),…

Android实战——系统字体库加载流程

Android 系统字体库指的是在Android设备上用于显示文本的字体集合。随着Android系统的更新,其对字体的支持也日益增强,允许开发者和用户更灵活地定制界面文字显示。 一、字体库介绍 1、字体库文件 字体库文件是指存储字体数据的文件,这些文件包含了创建文本字符所需的所有…

嵌入式乐鑫音频项目“无声”问题深度调试复盘与方法论总结

前言&#xff1a;一场典型的“工程师寻踪之旅” 本次调试始于一个看似简单却极其顽固的问题&#xff1a;在一个基于乐鑫ESP-ADF&#xff08;音频开发框架&#xff09;的DuerOS示例项目中&#xff0c;移植到M5Stack ATOMIC Echo Base硬件上后&#xff0c;程序能够成功编译、烧录…

地下安全防线:电缆通道防外破地钉如何守护城市隐形生命线

在繁华都市的柏油马路之下、在静谧乡村的泥土深处&#xff0c;纵横交错的地下管线如同城市与乡村的 “隐形生命线”&#xff0c;承载着电力输送、供水供气、通信传输等重要功能&#xff0c;默默维系着现代社会的正常运转。然而&#xff0c;这条 “生命线” 正面临着诸多潜在威胁…

linux时间同步方案

yum install chrony -y # 配置 chrony 使用国内服务器 sed -i s/^pool.*pool.ntp.org/#&/ /etc/chrony.conf cat >> /etc/chrony.conf <<EOF server ntp.aliyun.com iburst server ntp.tencent.com iburst server ntp.ntsc.ac.cn iburst server time1.cloud.t…

C语言笔记(鹏哥)上课板书+课件汇总(KMP算法的动态规划简易处理+字符函数和字符串函数)

一、目录 kmp动态规划简易处理next数组字符函数与字符串函数 一、目录二、引言C语⾔标准库中提供了⼀系列库函数 三、字符分类函数&#xff08;字符相关的函数&#xff09;推荐一个网站 四、字符转换函数&#xff08;字符相关的函数&#xff09;五、strlen&#xff08;字符串相…

Java大模型开发入门 (13/15):拥抱官方标准 - Spring AI框架入门与实践

前言 到目前为止&#xff0c;我们整个系列的旅程都是在功能强大的LangChain4j框架上构建的。它就像一个装备齐全的“瑞士军刀”&#xff0c;为我们提供了构建RAG和Agents所需的所有底层和高层工具。 然而&#xff0c;在Java企业级开发的世界里&#xff0c;有一个名字我们永远…

Github搜索案例

今天的内容是这个案例的实现&#xff0c;以及其中涉及到的内容&#xff0c;需要全部掌握&#xff0c;比如ref&#xff0c;受控组件&#xff0c;props在组件之中的传递&#xff0c;以及Pubsub包的使用这些前端React框架有关的内容。现在进入正题 1.github搜索案例&#xff08;a…

Vue3学习(生命周期,hooks,axios的简单讲解)

一&#xff0c;前言 继续努力&#xff0c;南方见。 二&#xff0c;生命周期 1.对生命周期的理解 例如&#xff1a;人的生命周期&#xff0c;出生&#xff0c;经历&#xff0c;死亡 组件的话就是&#xff0c;创建&#xff0c;挂载&#xff0c;更新&#xff0c;销毁。***在特…

Pytorch实战四 基于 VGG net 搭建一个串联的神经网络结构

系列文章目录 文章目录 系列文章目录前言一、VGG类的搭建1.源码2.初始化类2.1 初始化函数2.2 前向传播函数 forward(self,x) 二、卷积补充卷积 前言 对于标准的 VGG net 输入图像的尺寸是 24 x 24,进行 32 维的下采样之后得到一个 7 x 7 的特征图&#xff0c;然后用 FC 层完成分…

大学专业解读——计算机

我们继续&#xff0c;讲讲排名第二流行的新工科专业——计算机。说到计算机&#xff0c;可能所有人都知道&#xff0c;但具体到细分的专业类别&#xff0c;除了计算机科学&#xff0c;其实大多数人都是不了解的。 序&#xff1a; 计算机主要有如下几个专业&#xff1a; 计算机…

Bootstrap 5学习教程,从入门到精通, Bootstrap 5 列表组(List Group)语法知识点及案例(14)

Bootstrap 5 列表组(List Group)语法知识点及案例 一、列表组基础语法 列表组是Bootstrap中用于显示一系列内容的灵活组件&#xff0c;常用于显示菜单、导航或任何项目列表。 基本列表组结构 <ul class"list-group"><li class"list-group-item&quo…

FPGA基础 -- Verilog 命名事件

Verilog 的“命名事件&#xff08;Named Events&#xff09;”机制 进行一次系统、专业的培训。该机制在 Verilog 中是比较冷门但重要的仿真控制特性&#xff0c;主要用于 模块间同步、行为仿真触发、事件通信&#xff0c;在复杂的 Testbench、行为模型中尤为重要。 一、命名事…

《Go语言圣经》结构体

《Go语言圣经》结构体 一、结构体指针的高效应用 在处理大型结构体时&#xff0c;为避免内存复制&#xff0c;通常使用指针传递和返回结构体&#xff1a; // 通过指针传入结构体&#xff0c;避免值拷贝 func Bonus(e *Employee, percent int) int {return e.Salary * percen…