March 19, 2020

react-router结构图解析

前言

最近在看一下react-router的源码,看到react-rouer的功能分散到几个packages,也依赖基础包hisotry,如果完整源码分析,可能会有点多,所以这次就弄一个张图来说明一下react-router的使用。估计再过一阵子,v6版本就要发了,这个图是v5.x版本的,有点悲催。下面直接上图,主要说是在浏览器端运行的react-router

react-router结构图形 _1_.jpg

图有点点长,先来简单说一下不同颜色的区域表示什么:

  • 蓝色:使用的用户,也就是开发者
  • 绿色:不同的组件
  • 黄色:通常是指一些方法
  • 橙色:创建对象的函数
  • 白色:浏览器基础对象

左边纵向的一列: react-router,react-router-dom,history表示不同的库,而window就是浏览器。

下面就从对底层说起,捋一下这张图。

window

通常来说,路由库大部分基于两种:

一种是window.history对象来控制url的改变

另外一种是通过location.hash值来控制url的改变;

现在应该大部分都是用window.history,如果要兼容部分低版本浏览器,可能就需要到location.hash。而这两者都需要到location的支持,才能获取更详细的信息。

history

需要注意的是,这是一个库,并不是window.history的对象。

这个库是对页面路径的操作进行封装,目前是独立一个仓库,github 地址,支持上面所说的window.historylocation.hash的两种路由情况;

分别对应BrowserHistoryHashHistory的创建函数,通过这两个创建函数创建出来的对象,都具有相同的API调用,因为history这个库对这两种情况的路由进行了适配。通常这些通用的方法包含:

1
2
3
4
5
6
7
history.push()
history.replace()
history.go()
history.back()
// ... 等等

除此之外,还有定义一些路由过度的`promot`的逻辑等。

除此之外,还有还封装了MemoryHistory的创建方法,看起来,是给到react-router-native与部分测试的时候使用的。

react-router-dom

这个是react-router的仓库其中一个package,是专门针对浏览器处理的路由封装。

通常我们是在这个库里面指定使用哪一种路由方式,BrowserHistory or hashHistory:

1
2
3
4
import { BrowserRouter as Router } from 'react-router-dom'

// or
import { HashRouter as Router } from 'react-router-dom'

而指定路由方式的时候,则调用hisotry的两种不同createHistory方法;获取创建的hisotry返回对象。

react-router-dom处理指定路由方式之外,还提供LinkNavLink的组件;通常来说;这两个组件用于跳转到不同的路由,这个时候跳转也是调用createHistory返回对象的API,push或者replace

react-router

react-router通常是开发者直接调用的入口,例如路由组件的分发,当前参数获取等。

我们知道,从react-router-dom引入BrowserRouter或者HashRouter来指定路由方式;其实这个时候,也返回一个Router的组件,用于渲染页面路由的根组件。

Router的根组件是后面所有组件的基础;后续所有组件,必须在这个根组件下。Router,Switch等组件层层嵌套。在这个过程中,RouterContext是在整个组件过程中存在,这个context就是用于不同组件间的数据共享;通常这个context的数据为:

1
2
3
4
5
{
    history, // createHistory 返回的对象
    location, // 当前路径的信息
    match // 当前路由匹配的信息
}

嵌套路由之间也是共用这个context,从而达到路由数据之间的传递;而hooks的调用也是基于useContext来演变成不同的hooks,例如useHistory,useLocation等。

react-router-domLink更改的路由,怎么通知到context更改呢?

createHistory的时候,返回的history对象️监听方法listen;而根组件Router监听该方法,若发生改变,则更改RouterContext的内容;从而做到不同组件间的数据通信。

小结

这篇文章对代码描述的不多,主要是对流程的处理进行梳理;了解到不同库之间的协作,与数据通信的技巧。具体实现还是得看源码。希望能够一图胜千言!!!喜欢给个star~