vue spa应用中的路由缓存问题与解决方案

2020-06-13 10:39:34易采站长站整理

import PropTypes from 'prop-types';
import {matchPath} from 'react-router';
import {Route} from 'react-router-dom';

class RouteCache extends React.Component {

static propTypes = {
include: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.array
])
};

cache = {}; //缓存已加载过的组件

render() {
const {children, include = []} = this.props;

return React.Children.map(children, child => {
if (React.isValidElement(child)) { // 验证是否为是react element
const {path} = child.props;
const match = matchPath(location.pathname, {...child.props, path});

if (match && (include === true || include.includes(path))) {
//如果匹配,则将对应path的computedMatch属性加入cache对象里
//当include为true时,缓存全部组件,当include为数组时缓存对应组件
this.cache[path] = {computedMatch: match};
}

//可以在computedMatch里追加入一个display属性,可以在路由组件的props.match拿到
const cloneProps = this.cache[path] && Object.assign(this.cache[path].computedMatch, {display: match ? 'block' : 'none'});

return <div style={{display: match ? 'block' : 'none'}}>{React.cloneElement(child, {computedMatch: cloneProps})}</div>;
}

return null;
});
}
}

// 使用
<RouteCache include={['/login', '/home']}>
<Route path="/login" component={Login} />
<Route path="/home" component={App} />
</RouteCache>

在阅读了源码后,我们知道Route组件会根据它的this.props.computedMatch来判断是否要渲染该组件。

我们在组件内部创建一个cache对象,将已经匹配到的组件的computedMatch属性写入该缓存对象中。这样即使当url不再匹配时,也能通过读取cache对象中该路径的值,并使用React .cloneElement方法将computedMatch属性赋值给组件的props。这样已缓存过的路由组件就会被一直渲染出来,组件就不会被卸载掉。

因为组件内部可能会包裹多个路由组件,所以使用React.Children.map方法将内部包含的子组件都循环返回。

为了UI与路由对应显示正确,我们通过当前的计算得出的match属性,来隐藏掉不匹配的组件,只为我们展示匹配的组件即可。如果你不想在组件外再套一层div,也可以在组件内部通过this.props.match中的display属性来切换显示组件。

仿照vue keep alive的形式,设置一个 include 参数API。当参数为true时缓存内部的所有子组件,当参数为数组时则缓存对应的path路径组件。

使用效果

在最初时,从未被url匹配过的组件不会被渲染,里面的dom结构是空的。