基于React Router的SPA单Route下多view切换方案
场景:针对查询消息历史记录,广播消息事件下有多个群,每个群下有对应的动作,三个view之间的关系如下
eventlist => roomlist => actionlist,点击某个event到达roomlist,点击某个room到达actionlist。因为这三者(eventlist, roomlist, actionlist)依赖关系比较强,而且会被多个地方调用,他们三者就封装为一个组件(Component)了。随着点击的深入,如何返回到上一级就变成现实的问题了,即使你在组件中提供了返回按钮,也不能保证用户那天不点击浏览器的返回按钮。
现有解决方案:
-
使用弹出框
这个在PC端还行,在H5端反复的弹出框就显得蛋疼了, say no。
-
服务端渲染
依靠服务端渲染,由服务端提供基础路由。改动量太大,say no。
-
改成多页应用
这个和服务端渲染类似,只是基础路由由文件夹路径提供而已,改动还是大,say no。
-
使用React-Router的childRoutes属性,定义不同路由指向不同的Component,但是面临的问题就是要把组件打散,分配配置每个child路由对应的Compoent,也就是说这里的公共组件至少拆分成三个,而且还不好解决组件共用问题,fuck,这我哪能受得了,say no。
最终方案:BrowerHistory + hash
BrowerHistory提供基础路由,比如这里的广播消息,hash负责分开的试图,这里定义两个hash:#roomlist和#actionlist,hash为空代表进入的是eventlist view。
注意,这里我说的是hash,不是HashHistory,因为React-Router本身只提供一种路由方案,不能混合使用,这里的hash是浏览器原生的location hash。
关于BrowerHistory的配置和用途这里不做说明,下面开始我们的路由之旅。
首先进入到eventlist view。由于点击某个event要进入roomlist,我们绑定下事件:
<span className={styles.process} onClick={this.viewrooms.bind(this, item)} >
{item.ext.status}
</span>
viewrooms方法中修改URL中hash
viewlogrooms(item){
history.pushState({}, '', '#roomlist');
let room_ids = item.room_ids;
queryrooms({room_ids})
}
注意:这里我们我们是前进的过程,使用的是history.pushState,它不会触发window的popstate事件,popstate触发我们用在浏览器点击返回的时候使用。关于onpopstate更多资料,请参考 https://developer.mozilla.org/zh-CN/docs/Web/API/Window/onpopstate 。
而在这里增加hash会触发React-Router监听,在这里我们做下保护,如果有hash则不执行,这样带来的问题是不能带上hash去刷新了。处理方法很暴力,加上location.hash = ''; ,这样就能重新返回到基础路由上了。
{
path: urlprefix + '/ctrldeskbroadcast',
name: 'ctrldeskbroadcast',
getComponent(nextState, cb) {
if (location.hash) {
location.hash = '';
return false;
}
require.ensure([], require => {
registerModel(app, require('./models/ctrldeskbroadcast'));
cb(null, require('./routes/ctrldeskbroadcast'));
});
},
},
当用户点击浏览器返回按钮时候,会触发onpopstate事件,我们在这里做监听,对view进行切换
window.onpopstate = ()=> {
if(this._isMounted) {
const { hash } = location;
...
this.refs.logevent.handlecancelfun();
}
}
这样就能够完美的实现单个路由下多个view的切换了。