前言
在前端单页面应用里面,路由是比较重要的部分,笔者的上一篇博文简单的路由介绍了简单的路由内部机制,本文则将分析react-router的内部机制。
介绍
react-router为react提供路由管理,为基于jsx格式的app系统提供了方便的切换页面功能。
它在前端提供给了2种方式,通过hashchange或者浏览器原生的history api进行地址更新,上一篇介绍了hash的方式,本文则以history api的形式切入分析。
代码剖析
路由配置
react-router本生为react组建,内部组建如Router,Route,IndexRoute,Redirect,Link等。
以下是摘自react-router example的路由配置
<Router history={withExampleBasename(browserHistory,__dirname)}> <Route path="/" component={App}> <IndexRoute component={Index}/> <Route path="/about" component={About}/> <Route path="users" component={Users}> <IndexRoute component={UsersIndex}/> <Route path=":id" component={User}/> </Route> </Route> </Router>
对应结构图
在初始化过程中他会以children形式读入Router生命周期内,在被转化为数组,此时它内部的结构如下
路由初始化
初始化browserHistory对象
react-router依赖history^2.0模块生成的history对象,然后在Router生命周期componentWillMount中加入对应的封装如basename,query
useQueries.js 对history对象内的方法进行封装
function listen(listener) { return history.listen(function (location) { listener(addQuery(location)) }) } // Override all write methods with query-aware versions. function push(location) { history.push(appendQuery(location,location.query)) }
useBasename.js 对history对象内的方法进行封装
function listen(listener) { return history.listen(function (location) { listener(addBasename(location)) }) } // Override all write methods with basename-aware versions. function push(location) { history.push(prependBasename(location)) }
Router.js 对history对象增加setRouteLeaveHook钩子函数以及isActive函数
最终生成router对象 以this.router = router存在Router组建内部,this.history 已过时(issues),不建议使用
初始化监听事件
this._unlisten = transitionManager.listen(function (error,state) { if (error) { _this.handleError(error); } else { _this.setState(state,_this.props.onUpdate); } }); function listen(listener) { changeListeners.push(listener); if (location) { listener(location); } else { var _location = getCurrentLocation(); allKeys = [_location.key]; updateLocation(_location); }
此时整体初始化完毕
改变路由
<Link to="about" activeStyle={ACTIVE}>/</Link>
以一次Link点击为例
触发Link组建的handleClick方法
拼装location对象
改变url栏的地址
调用updateLocation 触发changeListeners内的所有监听事件
回调函数内调用match方法,根据location对象正则匹配router对象,匹配出对应的组建 执行runLeaveHooks钩子
调用Router组建的setState(nextstate)
调用react.createElement
完成渲染
简洁的流转图
放上一个本人总结的一个简单流转过程图
详细流程图
对简洁的流程图熟悉之后,则可深入了解内部机制的细节如下图
归纳
虽然源码繁琐复杂,但是内部的核心仍是围绕着下面3块动作做一系列的封装.
注册监听事件:封装history对象的,生成router对象存储在Router内,并通过其注册监听事件,绑定相应的回调函数
触发监听事件:通过Link/browserHistory.push/浏览器回退快进/dom ready 等四种方式触发回调函数
回调函数: Router内的setState(next)最终触发react.createElement进而更新UI
最后
本文有什么不完善的地方,或者流程图有待改进的地方,敬请斧正。