Umi3.0实现类似Keep-Alive方式进行多标签切换

1,211 阅读1分钟

背景

由于react-router没有Keep-Alive的实现方式,所以Umi3也并没有这方面实现方式,尽管在Umi的github的issue中也有大多数的用户提了这方面的解决方式,比如react-keep-alive组件已经实现了,但是对于Umi3的高度封装,并没有办法对每个引入的组件进行设置,也google了一下解决方案。通过获取route中的组件,然后在redux中进行缓存。

实现

1. 获取props组件,并且获取route中的组件

// 获取所有routes
const getRoutes = (routes) => {
    const rs = routes || props.route.routes;
    return rs?.reduce((keys, item) => {
      keys.push(item);
      if (item.routes) {
        return keys.concat(getRoutes(item.routes));
      }
      return keys;
    }, []);
}

// 获取缓存组件 Umi3 bug 不能直接缓存children 需找出组件来缓存,但不支持嵌套路由
const getComponent = (routes, pathname) => {
  // 普通路由
  let routeIndex = routes.findIndex((r) => r.path === pathname);

  // 约定式路由
  if (routeIndex === -1 && pathname.includes(':')) {
    routes.forEach((r, index) => {
      if (r.path.includes(':')) {
        if (r.path.substr(0, r.path.indexOf(':')) === pathname.substr(0, pathname.indexOf(':'))) {
          routeIndex = index;
        }
      }
    });
  }

  return routeIndex > -1 ? { ...routes[routeIndex], routeIndex } : undefined;
};

const [routeList] = useState(getRoutes);
const routeInfo = getComponent(routeList, props.location.pathname);  // 对应path下的component

2.页面改变时对不同的路由下的component进行缓存

const onPageChange = (e) => {
    if (e.pathname !== '/') {
      setActiveKey(e.pathname)
      if (dispatch) {
        const element = React.createElement(routeInfo.component);
        dispatch({
          type: 'layout/saveRoute',
          payload: {
            name: routeInfo.name,
            path: location.pathname,
            children: element
          }
        })
      }
    }
  }

3.layout的model实现


const LayoutModel = {
  namespace: 'layout',
  state: {
    routes: []
  },
  effects: {
    *saveRoute({ payload, }, { put, select }) {
      const { routes } = yield select((state) => state.layout);
      const index = routes.findIndex((route) => route.path === payload.path);
      let newRoutes = routes;
      if (index === -1) {
        newRoutes = [...routes, payload]
      }
      yield put({ type: 'save', payload: { routes: newRoutes } })
    },
    *deleteRoute({ payload }, { put, select }) {
      const { routes } = yield select((state) => state.layout);
      const index = routes.findIndex((route) => route.path === payload.path);
      let newRoutes = routes;
      if (index > -1 && newRoutes.length > 1) {
        newRoutes.splice(index, 1);
      }
      yield put({ type: 'save', payload: { routes: newRoutes } })
    },
    *clearRoute(_, { put }) {
      yield put({ type: 'save', payload: { routes: [] } })
    }
  },
  reducers: {
    save(state, { payload }) {
      return { ...state, ...payload };
    }
  }
}

export default LayoutModel;

4.Tabs标签操作设置

const onEdit = (targetKey, action) => {
    if ('remove' === action) {
      dispatch({ type: 'layout/deleteRoute', payload: { path: targetKey } });
      if (routes.length >= 1) {
        setActiveKey(routes[routes.length - 1].path);
        window.history.pushState('', '', routes[routes.length - 1].path)
      }
    }
  };

总结

通过这种方式的实现,基本可以完成标签的缓存,主要是对redux中进行缓存的方式。

点击查看 源码