简述:实现一个基于react18的模板项目,支持路由keepalive,历史访问页签

709 阅读2分钟

一、概览

实现一个支持路由keepAlive,类似浏览器页签功能的react的模板项目。具体效果如下所示:

example.gif

预览项目已经部署到了vercel。在线体验(速度较慢的话,可能访问阿里云在线

项目源码地址

二、涉及技术

三、核心功能

  1. 类似umi的权限配置管理方案,可方便的对权限进行处理。
  2. 灵活的路由布局配置,可选择在左侧还是在头部。
  3. 类似浏览器页签的历史访问路径,支持拖拽,刷新,关闭等操作。
  4. 路由keepAlive的hack实现,能够缓存用户操作。
  5. 便捷的layout配置方案,类似antd-pro与umi的结合。

四、部分功能实现原理和使用方式

4.1 keepAlive

keepAlive是一个hack实现。在react想要实现keepAlive本质上就是要保证组件不被卸载。

组件是否被卸载的判断依据是,在update流程中,前后的两棵树中是否都具有此节点。如果有的节点在本次的render中没有返回,那么就会被打上删除的标识,相应的此节点也就会被卸载。

所以核心是,我们要保证组件不被卸载,查看核心代码

4.2 layout配置

layout的配置方式类似于umi + antd-pro的使用方式,对外暴露配置文件,在此文件中进行相应的选项配置即可。

// layout配置
export const layoutSettings: LayoutProProps = {
    logo: IconLogo,
    headerRight: <HeaderRight />,
    headerHeight: 60,
    siderWidth: 300,
    defaultCollapsed: false,
    maxLength: 10
    // isTabs:false
    // ...
}

4.3 项目初始化设置

对于希望在项目全局使用的信息或者组件加载出来前使用的数据,使用方式同umi一致:

// 初始化配置
export const getInitialState = async (): Promise<InitialStateType> => {
    const values: InitialStateType = {
        settings: layoutSettings,
        userInfo: {},
        accessInfo: []
    }

    // 获取用户登录信息
    // const userRes = await reqGetUserInfo()
    // if (!userRes.result) {
    // 	// 跳转登录链接
    // 	window.location.href = config.loginUrl
    // 	return values
    // } else if (userRes.message) {
    // 	message.error(userRes.message)
    // }

    values.userInfo = {
        nickName: 'thomas-void'
    }
    // values.settings.watermark = {
    // 	text: values.userInfo.nickName
    // }

    return values
}

在需要使用的地方:

const { globalState } = useGlobal()

4.4 路由配置

针对路由,提供了多种配置方案:

interface RouteExtension {
    /* 路由显示icon */
    icon?: ReactNode
    /* 在菜单中显示的名称,如果没有name则不在菜单中显示 */
    name?: string
    /* 权限相关 */
    access?: string
    /* 是否要开启keep-alive, 默认是false */
    keepAlive?: boolean
    /* layout布局相关,默认true */
    layout?:
        | boolean
        | {
            /* 渲染左边的sider组件 */
            leftSiderRender?: boolean
            /* 是否渲染到左边的菜单项中 */
            leftItemRender?: boolean
            /* 是否渲染头部header组件 */
            headerRender?: boolean
            /* 是否渲染到头部的菜单项目中 */
            topItemRender?: boolean
          }
    /* 扩展的children */
    children?: RouteConfig[]
}