要想实现页面缓存,在React应用中通常有以下两种解决方案。
第一种方案:状态存在内存中。比如使用Redux、Mobx或自定义内存变量,在页面离开前,将页面内用户产生的数据存储在内存中,将页面“快照”下来,并销毁页面的DOM节点。这样虽然页面的DOM节点被销毁,但是在重新挂载页面的时候,可从内存中将各节点通过“快照”进行恢复。这种方案的优点主要是路由切换DOM节点销毁,DOM节点数量可控,而其缺点也同样明显,将引入额外开发量,无论是在业务上还是在框架上,都将引入额外的开发成本。
第二种方案:不销毁DOM节点,对其进行缓存。这种方案的优点主要是可以降低开发成本。虽然页面的DOM节点会保留,但是由于减少了页面离开时的“快照”过程,业务开发的流程侵入性较小,并不需要在页面销毁时将页面状态保存到内存中。通过缓存DOM节点,一来无须框架层面的额外开发,二来也不会侵入业务代码,仅需要对路由的行为做一些改变。但这类方案也同样存在缺点,由于一般通过CSS方式缓存DOM隐藏页面,因此页面的DOM节点数量会增多。但若控制得当,DOM的节点数量也在可控范围内。
import * as React from "react";
import { Route, RouteChildrenProps, RouteProps } from "react-router";
import { omit } from "lodash";
import MemoChildrenWithRouteMatch, {
MemoChildrenWithRouteMatchExact
} from "./Cache";
import Remount from "./Remount";
interface Props {
forceHide?: boolean;
shouldReMount?: boolean;
shouldDestroyDomWhenNotMatch?: boolean;
shouldMatchExact?: boolean;
}
export default function CacheRoute(props: RouteProps & Props) {
const routeHadRenderRef = React.useRef(false);
return (
<Route
{...omit(props, "component", "render", "children")}
children={(routeProps: RouteChildrenProps) => {
const Component = props.component;
const routeMatch = routeProps.match;
let match = !!routeMatch;
if (props.shouldMatchExact) {
match = routeMatch && routeMatch.isExact;
}
if (props.shouldDestroyDomWhenNotMatch) {
if (!match) routeHadRenderRef.current = false;
// 按正常逻辑
if (props.render) {
return match && props.render(routeProps);
}
return (
match && Component && React.createElement(Component, routeProps)
);
} else {
const matchStyle = {
// 隐藏
display: match && !props.forceHide ? "block" : "none"
};
if (match && !routeHadRenderRef.current) {
routeHadRenderRef.current = true;
}
let shouldRender = true;
if (!match && !routeHadRenderRef.current) {
shouldRender = false;
}
const MemoCache = props.shouldMatchExact
? MemoChildrenWithRouteMatchExact
: MemoChildrenWithRouteMatch;
// css隐藏保留dom
let component;
if (props.render) {
component = props.render(routeProps);
} else {
component = <Component {...routeProps} />;
}
return (
shouldRender && (
<div style={matchStyle}>
{/*提供remount能力*/}
<Remount shouldRemountComponent={props.shouldReMount}>
{/*提供渲染优化*/}
<MemoCache {...routeProps}>{component}</MemoCache>
</Remount>
</div>
)
);
}
}}
/>
);
}
源码
。