要想实现页面缓存,在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隐藏保留domlet 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>));}}}/>);}
源码
。
