主要讲述一下在实现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的路径上。
参考
参考文章
参考项目