react 实现页面状态缓存(keep-alive)

前言:

因为 react、vue都是单页面应用,路由跳转时,就会销毁上一个页面的组件。但是有些项目不想被销毁,想保存状态。

比如:h5项目跳转其他页面返回时,页面状态不丢失。设想一个 页面我滑倒了中间,然后跳转到 详情页然后 返回,之前的页面刷新了,回到顶部了肯定不行(搜索条件之类的消失了,滚动条回到顶部了)!

比如:pc端项目后台管理项目里点击时 打开一个页签,页签切换,状态页会丢失。每次切换页签都重新请求了接口。

解决方案:

方案调研:

经过我的调研:
我找到的 第三放库有:
react-activation
umi-plugin-keep-alive
umi-plugin-keep-alive-tabs
react-keepalive-router
react-router-cache-route

redux、dva等(状态共享插件)

react-keepalive-router、react-router-cache-route:

react-keepalive-router、react-router-cache-route 是个人开发的,github上issues里的建议也没及时回答。所以我就放弃了,没考虑。

Offscreen:

Offscreen是react 18.x出的实验性api,所以我也放弃了。可以看react Offscreen
不过 此api如果正式使用的话,应该是最好的选择。其原理就是 把页面 隐藏起来,不销毁组件树。其实其他 插件原理也是这样。

在这里插入图片描述

umi-plugin-keep-alive、umi-plugin-keep-alive-tabs:

umi-plugin-keep-alive、umi-plugin-keep-alive-tabs 是umi里的,是阿里开发的,优先考虑的就是这个,但看了 文档发现 它基于 react-activation。而且 作者也让关注 。
npm umi-plugin-keep-alive

umi-plugin-keep-alive api
其实 如果你项目是 umi的话 用 umi-plugin-keep-alive也可以(低版本umi可能不行)。antd-pro
若依等等基于umi搭建的库都可以使用umi-plugin-keep-alive。这个插件 和umi绑定,所以我也放弃了,但应该也可以单独使用(我没试过)。

redux等状态共享插件,需要项目搭建时就使用,原理就是,页面里不写 useState和state全都放到 store里。然后对整个store缓存。每次进入页面 判断一下有缓存就走缓存,没有重新请求。像redux之类的都有持久化插件,配合持久化插件就很容易实现。缺点是繁琐,且破坏了 不优雅,页面里不能写状态。而且 还要额外 记录滚动条的位置。

react-activation:

所以综上我选择了 react-activation 它是路由级别的缓存。
在这里插入图片描述

react-activation基础使用步骤及配置:

1.安装 react-activation

yarn add react-activation
# or
npm install react-activation

(可选,推荐)在 .babelrc 中添加 react-activation/babel 插件
该插件在编译过程中为每个JSX元素添加了一个 _nk 属性,以帮助 react-activation 运行时根据 react-node-key 的渲染位置生成唯一的标识符。

{
  "plugins": [
    "react-activation/babel"
  ]
}

如果不想使用Babel,建议每个 声明一个全局唯一不变的 cacheKey 属性,以保证该高速缓存的稳定性,如下所示:

<KeepAlive cacheKey="UNIQUE_ID" />

2.import KeepAlive 然后包裹要缓存的组件 或者元素。

官网示例:

// App.js

import React, { useState } from 'react'
import KeepAlive from 'react-activation'

function Counter() {
  const [count, setCount] = useState(0)

  return (
    <div>
      <p>count: {count}</p>
      <button onClick={() => setCount(count => count + 1)}>Add</button>
    </div>
  )
}

function App() {
  const [show, setShow] = useState(true)

  return (
    <div>
      <button onClick={() => setShow(show => !show)}>Toggle</button>
      {show && (
        <KeepAlive>
          <Counter />
        </KeepAlive>
      )}
    </div>
  )
}

export default App

配合路由使用react-activation

1.isCache是自定义的属性,用来标识是否 缓存。true就是改路由需要缓存。

  {
    path: "/",
    component: <Initial />,
    title: "主页",
    name: "initPage",
    isCache: true,
    cacheKey: "home",
  }

路由配置可以参考:
react create-react-app v5 配置路由(报错及注意事项)
App.js 入口文件 或者 路由配置页面里 封装一层 根据 isCache值来确定是否使用 keepAlive包裹。如下:

import React from "react";
import { BrowserRouter, Routes, Route, HashRouter } from "react-router-dom";
import routes from "./routes.js";
import KeepAlive from "react-activation";
// 封装一层 专门负责显示页面标题
const DomTitle = ({ route }) => {
  const { title, component, isCache ,cacheKey,name} = route;
  document.title = title;
  if (isCache) {
    return <KeepAlive cacheKey={cacheKey} name={name}>{component}</KeepAlive>;
  }
  return <>{component}</>;
};
const App = () => {
  return (
    <HashRouter>
      <Routes>
        {routes.map((route) => (
          <Route
            key={route.path}
            path={route.path}
            //element={route.component   }
            // 专门负责显示页面标题
            element={<DomTitle route={route} />}
          />
        ))}
      </Routes>
    </HashRouter>
  );
};

还可以手动清除缓存

import {
  useActivate,
  useUnactivate,
  withActivation,
  withAliveScope,
  useAliveController,
} from "react-activation";
 const { drop, dropScope, clear, getCachingNodes } = useAliveController();
   console.log(getCachingNodes(), "缓存节点");
    //drop("homePage"); // 手动关闭某个页面
    // dropScope("detailPage");
    dropScope("homePage");  // 参数就是上面定义的  cacheKey

具体 api使用方法和注意事项请看:
具体api可以看(如果 github打不开可以看npm的,npm是英文的,可以用edge浏览器 翻译一下):
react-activation github 使用文档
react-activation npm 使用文档