主要讲述一下在实现React多页签过程中遇到的一些问题以及解决方案。

Vue 自带的 keep-alive 组件使得 Vue 本身就支持多页签的形式。但是 React 是不附带类似组件的,而且后续也没有添加类似功能的想法。所以我们需要自己实现一个多页签。

在时间有限的情况下,为了更顺利的完成这件事。必须先要将将要面临的问题给找出来,根据问题寻找答案,最后将答案汇总,才能实现最终的想要的效果。

问题

要实现一个多页签系统,需要考虑这么几个问题:

  • 多个页面可以同时存在
  • 从一个列表页可以打开多个详情页,每个详情页都需要拥有一个独立的页面
  • 存在多个详情页时,每个详情页的标题应该不同,需要动态设置标题
  • 页面可以被关闭,且当前页关闭后能切换到前一个页面
  • 默认添加首页,首页不可被关闭
  • 页面之间可以相互通信。比如:详情页更新了数据,列表页如果存在,则列表页需要更新数据
  • 打开某个页签后刷新,被打开的页签仍需被保留
  • 可以在系统里的任意一个位置触发路由跳转
  • 可以直接通过修改路由url直接访问到指定页面
  • 当输入了不可识别的url时,需要重定向到404页或者首页
  • 给列表页添加了参数导致出现两个列表页:如何防止由于手动给列表页或者其他不应该带参数的路径添加路径,导致出现两个列表页的情况
  • ……

解决方案

首先有一个大方向,想要做成多页签,那么切换路由的时候必然是不伴随页面刷新的。所以可以确定一个大致的方向是使用 HashRouter

多个页面可以同时存在

目前网上统一口风,都是使用antd的Tabs组件实现,而我们需要展示的多个页面在Tabs看来就是多个组件。

从一个列表页可以打开多个详情页,每个详情页都需要拥有一个独立的页面

Tabs组件会使用唯一key对应唯一组件的方式实现面板的渲染和切换,而多个详情页实际上使用的是同一个模板。为了达到详情页之间互相隔离的效果,不同的详情页就必须要使用不同的key值。此时就需要页面本身的key加上页面的params,两者组合生成唯一key。

存在多个详情页时,每个详情页的标题应该不同,需要动态设置标题

全局提供一个 setTitle 方法,调用时可更改当前页签的标题。

页面可以被关闭,且当前页关闭后能切换到前一个页面

Tabs组件本身是支持关闭页签的,而我们要做的就是在关闭页签之前,先切换一下活跃的tab key值就好了。

默认添加首页,首页不可被关闭

初始时默认向面板列表里插入一项数据。

页面之间可以相互通信

使用发布订阅(trigger + on)的方式来实现页面的相互通信。

打开某个页签后刷新,被打开的页签仍需被保留

其实实现的思路并不是保留,而是通过识别hash然后向面板列表插入hash对应的数据。

可以在系统里的任意一个位置触发路由跳转

可以使用 react-router-dom 提供 history.push 或者 history.replace 方法进行hash的切换。

可以直接通过修改路由url直接访问到指定页面

打开某个页签后刷新,被打开的页签仍需被保留

当输入了不可识别的url时,需要重定向到404页或者首页

在识别了hash值之后,先不急着向面板列表里插入数据,而是先判断hash值是否正确,是否可用(系统中是否存在该hash路由)。如果发现hash是不可用的,则需要 replace 方法更新hash值为首页或者404页的路径。

给列表页添加了参数导致出现两个列表页

正常来说,列表页应该是不携带参数的,同一个列表页不会被渲染多次。但是由于存在 key + params 生成唯一key的机制,所以可能会将带参数的列表页误识别为另一个页。

此时需要在配置里添加 exact 属性,用于标识当前页是否允许存在参数,当发现不应该携带了参数的路径携带了参数时,使用replace重定向到不带params的路径上。

参考

参考文章

  1. react多页签页面缓存.

参考项目

  1. rodchen-king/ant-design-pro-v2.
  2. hsl947/react-antd-multi-tabs-admin.