react-router-dom6-keepalive
Version:
```js import KeepAlive from "react-router-dom6-keepalive" ``` ## 使用方法 ### h5形式 ```js import KeepAlive from "react-router-dom6-keepalive" //路由配置 const routeElements = useRoutes(routes); //配置tabBar页面,tabBar页面会一直缓存,pop它也不会销毁。其它页面pop后就会销毁。达到类似微信小程序的路由效果。 cons
235 lines (203 loc) • 8.48 kB
JavaScript
import ReactDOM from "react-dom";
import React, { memo, useEffect, useLayoutEffect, useMemo, useRef, Suspense } from "react";
import { useLocation, Navigate } from "react-router-dom";
/**
* 1、pop(页面回退)的时候删除上一页的缓存
* 1-1、路由变化的时候,哪些路由不需要删除
* 有两个模式:
* 1、用黑白名单
* 2、pop自动删除缓存页面
* 什么参数都不传,则所有页面都缓存
* */
/* 钩子 */
const __ORANGE__KeepAliveArgs__ORANGE__ = {
pageShowFuns: [],
//页面显示钩子,*代表所有。
pageHidenFuns: [],
//页面隐藏钩子
lastActiveName: undefined //上一次显示的页面路由
}; // 渲染当前匹配的路由 不匹配的 利用createPortal 移动到 document.createElement('div') 里面
function _KeepAliveComponent({
children,
name,
renderDiv,
activeName,
isNeedSuspense,
SuspenseLoading
}) {
//缓存页面离屏渲染的 div
const aliveContainer = document.createElement("div");
aliveContainer.setAttribute("id", "keepalive-" + name);
/*无需设置样式*/
// aliveContainer.setAttribute("style", "width: 100%; height: 100%;");
// aliveContainer.children[0].scrollBy(0, 100);
const targetElement = useRef(aliveContainer);
const returnEleRef = useRef(isNeedSuspense ? /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React.createElement(Suspense, {
fallback: SuspenseLoading
}, children), targetElement.current, name) : /*#__PURE__*/ReactDOM.createPortal(children, targetElement.current, name));
useEffect(() => {
if (name === __ORANGE__KeepAliveArgs__ORANGE__.lastActiveName) {} // 渲染匹配的组件,执行页面显示hook
if (name === activeName) {
//执行页面隐藏钩子
__ORANGE__KeepAliveArgs__ORANGE__.pageHidenFuns.forEach(item => {
if (item.activeName === __ORANGE__KeepAliveArgs__ORANGE__.lastActiveName) {
item.callBack();
}
}); // 执行页面显示钩子
__ORANGE__KeepAliveArgs__ORANGE__.pageShowFuns.forEach(item => {
if (item.activeName === activeName || item.activeName === "*") {
item.callBack();
}
}); //改变上一次页面
__ORANGE__KeepAliveArgs__ORANGE__.lastActiveName = activeName;
renderDiv.current.appendChild(targetElement.current);
} else {
try {
// 移除不渲染的组件
renderDiv.current.removeChild(targetElement.current);
} catch (e) {}
}
}, [activeName, name, renderDiv]); // 把vnode 渲染到 aliveContainer 里面
return /*#__PURE__*/React.createElement(React.Fragment, null, returnEleRef.current);
}
export const KeepAliveComponent = /*#__PURE__*/memo(_KeepAliveComponent);
function KeepAlive({
exclude,
include,
activeName,
children,
isPopDelete = false,
//返回上一页的时候,删除当前页的缓存。否则不删除,直到超过最大缓存数
alwaysCacheRouts = [],
//控制哪些路由总是缓存,应用场景:tabar对应的页面。在isPopDelete=true时生效
maxLen = 10,
isNeedSuspense = false,
//用于懒加载的处理
SuspenseLoading = /*#__PURE__*/React.createElement("div", null, "loading") //懒加载时的loading
}) {
if (include && exclude) {
exclude = undefined;
console.warn("白名单和黑名单同时存在时,只会使用白名单。也就是说黑名单将会被忽略。");
}
if (include || exclude) {
//优先使用黑白名单
isPopDelete = false;
}
const lastActiveComponent = useRef(null);
const containerRef = useRef(null);
const components = useRef([]); //目前显示的是哪个页面的路由
const location = useLocation();
const nowShowPathname = activeName || location.pathname;
/**
* 判断一些不需要缓存的组件,比如重定向,直接重定向,不需要缓存
* 这里不能用type.name 判断组件是否是Navigate,因为生产版编译后type.name 都是t
* */
const isNoNeedCacheElement = useMemo(() => {
const navEle = /*#__PURE__*/React.createElement(Navigate, {
to: "/"
});
if (!children) return true;else if (navEle.type === children.props.children.type) return true;else if (children.props.value.outlet) {
if (navEle.type === children.props.value.outlet.props.children.type) return true;else if (children.props.value.outlet.props.value.outlet && navEle.type === children.props.value.outlet.props.value.outlet.props.children.type) return true;else return false;
} else return false;
}, [children]);
if (!isNoNeedCacheElement) {
// 缓存超过上限的 删除第一个缓存
if (components.current.length >= maxLen) {
components.current = components.current.slice(1);
} // 添加
let component = components.current.find(res => res.name === nowShowPathname);
let isNeedCache = true; //默认全都缓存
if (include) {
isNeedCache = !!include.includes(nowShowPathname);
} else if (exclude) {
isNeedCache = !exclude.includes(nowShowPathname);
}
if (!component) {
component = {
name: nowShowPathname,
ele: children,
isNeedCache,
scrollTop: 0
};
components.current = [...components.current, component];
} else {
//找到了缓存的页面
const pageIdx = components.current.indexOf(component) + 1;
const pageLen = components.current.length;
if (pageIdx < pageLen && !alwaysCacheRouts.includes(lastActiveComponent.current.name) && isPopDelete) {
//页面回退
//把最后一个给删掉
components.current = components.current.slice(0, components.current.length - 1);
} //把不需要缓存的页面删掉
else if (lastActiveComponent.current) {
//把不需要缓存的页面删掉
if (!lastActiveComponent.current.isNeedCache) {
components.current = components.current.filter(item => item !== lastActiveComponent.current);
}
}
}
lastActiveComponent.current = component;
} //滚动到上次离开到位置
useEffect(() => {
if (!children) return;
if (isNoNeedCacheElement) return;
const component = components.current.find(res => res.name === nowShowPathname);
if (component) {
const lastEle = containerRef.current;
document.getElementsByTagName("html")[0].scrollTop = component.scrollTop;
return () => {
component.scrollTop = -lastEle.getBoundingClientRect().top;
};
}
}, [children]);
if (!children) {
return /*#__PURE__*/React.createElement(React.Fragment, null);
} else if (isNoNeedCacheElement) {
return /*#__PURE__*/React.createElement(React.Fragment, null, children);
} else {
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
ref: containerRef,
id: "react-router-dom6-keepalive-container"
}), components.current.map(({
name,
ele
}) => /*#__PURE__*/React.createElement(KeepAliveComponent, {
activeName: nowShowPathname,
renderDiv: containerRef,
name: name,
key: name,
isNeedSuspense: isNeedSuspense,
SuspenseLoading: SuspenseLoading
}, ele)));
}
}
export default /*#__PURE__*/memo(KeepAlive);
export const onPageShow = function (activeName, callBack) {
if (__ORANGE__KeepAliveArgs__ORANGE__.pageShowFuns.some(item => item.activeName === activeName)) {
//提示使用第一个
console.warn("你已经注册了", activeName, "pageShow,默认使用第一个,请注意调用");
} else {
__ORANGE__KeepAliveArgs__ORANGE__.pageShowFuns.push({
activeName,
callBack
});
}
};
export const onPageHiden = function (activeName, callBack) {
if (__ORANGE__KeepAliveArgs__ORANGE__.pageHidenFuns.some(item => item.activeName === activeName)) {
//提示使用第一个
console.warn("你已经注册了", activeName, "pageHiden,默认使用第一个,请注意调用");
} else {
__ORANGE__KeepAliveArgs__ORANGE__.pageHidenFuns.push({
activeName,
callBack
});
}
};
export const removeKeeAliveHook = function (activeName, type = "show") {
if (type === "show") {
__ORANGE__KeepAliveArgs__ORANGE__.pageShowFuns = __ORANGE__KeepAliveArgs__ORANGE__.pageShowFuns.filter(item => item !== activeName);
} else {
__ORANGE__KeepAliveArgs__ORANGE__.pageHidenFuns = __ORANGE__KeepAliveArgs__ORANGE__.pageHidenFuns.filter(item => item !== activeName);
}
};