前言
在前端单页面应用里面,路由是比较重要的部分,笔者的上一篇博文简单的路由介绍了简单的路由内部机制,本文则将分析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方法
- 调用router对象 push方法
- 拼装location对象
- 改变url栏的地址
- 调用updateLocation 触发changeListeners内的所有监听事件
- 回调函数内调用match方法,根据location对象正则匹配router对象,匹配出对应的组建 执行runLeaveHooks钩子
- 调用Router组建的setState(nextstate)
- Router-context组建的render方法调用createElement
- 调用react.createElement
- 完成渲染
简洁的流转图
放上一个本人总结的一个简单流转过程图
详细流程图
对简洁的流程图熟悉之后,则可深入了解内部机制的细节如下图
归纳
虽然源码繁琐复杂,但是内部的核心仍是围绕着下面3块动作做一系列的封装.
- 注册监听事件:封装history对象的,生成router对象存储在Router内,并通过其注册监听事件,绑定相应的回调函数
- 触发监听事件:通过Link/browserHistory.push/浏览器回退快进/dom ready 等四种方式触发回调函数
- 回调函数: Router内的setState(next)最终触发react.createElement进而更新UI
最后
本文有什么不完善的地方,或者流程图有待改进的地方,敬请斧正。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。