React Router源码浅析

2022-09-26 10:42:40 浏览数 (1)

2020年的春节是一个多灾多难的春节,新型冠状病毒的出现折磨着每一个中国人的心,导致不少公司都安排节后在家办公,但是在这个时候,作为一名小前端也是要继续努力学习,所哟2020年的第一篇文章就从React Router的源码阅读开始,继续了解React的体系。


前言

为什么去看React Router的源码?

  • 了解React Router的实现原理
  • 如何监听路有变化以及渲染对应的组件

我一直认为,会用框架和用好框架是有很大的区别的,当用框架到一定程度的时候,就需要看看框架对应生态中那些不可获取的库,这样能加深在不同框架中同样的功能的优秀实现方案。

React Router是什么?

React Router是React团队开发的基于React框架架构所实现的路由库。

Github

React Router有多个版本。

一般前端写web页面多数是使用react-router-dom这个库,那么react-router和react-router-dom有什么区别呢?

其实react-router-dom是基于react-router再封装的一个带有React DOM组件的库,其中包括了Link、HashRouter、BrowserRouter等组件提供给开发者通过使用标签的方式控制路由跳转。


阅读须知

  • 源码阅读基于react-router和react-router-dom 5.2.1版本

React Router如何监听路由变化的?

通过查看源码发现,react-router使用了一个history的库来监听不同的路由变化,react-router支持我们使用hash和bowser两种路由规则,所以history这个库可以根据调用的api不同,来区分当前是监听不同的路由方式。

history

history这个库的内容并不在本文章的阅读范围内,有兴趣的可以自行查看。

我们开始逐步开始阅读源码。我们使用React Router的时候第一个了解的就是BrowserRouter和HashRouter这两个内置的组件。通过源码发现其实两个组件的实现是完全一样的,只是内部调用创建history实例的方式不一样,一个调用createHashHistory,另一个调用createBrowserHistory。

基于一个官方demo展开阅读:

代码语言:javascript复制
export default class App extends React.Component {


    render () {
        return (
            <Router>
                <div>
                    <ul>
                        <li>
                            <Link to="/">Home</Link>
                        </li>
                        <li>
                            <Link to="/about">About</Link>
                        </li>
                        <li>
                            <Link to="/dashboard">Dashboard</Link>
                        </li>
                    </ul>
                    <hr />
                    <Route exact path="/">
                        <Home />
                    </Route>
                    <Route path="/about">
                        <About />
                    </Route>
                    <Route path="/dashboard">
                        <Dashboard />
                    </Route>
                </div>
            </Router>
        );
    }

}

BrowserRouter.jsx

HashRouter.jsx

本篇文章是基于HashRouter进行阅读,实际上只是监听的事件不一样而已。

通过源码发现,HashRouter实例化了一个history的实例,并且将history实例通过props和children一起传入的Router组件当中。

接下来是Router组件。

computeRootMatch函数中,如果pathname !== "/"的下,isExact会为false,后续会用到

Route组件

接下来我们看看matchPath函数是如何判断当前的url是否命中当前Route组件的path的。

到这里,就是大概整体渲染的时候React Router做了什么事情。

总结:

  1. HashRouter
    1. 实例化history,调用createHashHistory
    2. 将children和history传入Router组件
  2. Router
    1. constructor周期内监听history的路由事件,将新的location存到Router的state中
    2. componentWillUnmount移除监听
    3. 使用Context包裹子组件(Provider),存入history、location、match(默认的命中对象)等。
  3. Route
    1. 使用Context,声明为Consumer,接收Router传入的值。
    2. 调用matchPath函数来判断当前Route的path是否命中当前url。
    3. 使用Context包裹子组件(Provider),将Router传递进来的参数以及命中结果等传入给Route包裹的子组件
    4. 渲染循序如下:
      1. 当前Route是否命中url
          1. 判断当前Route是否有子组件,有那么将渲染子组件,否则进入下一条
          2. 判断当前Route是否有component参数,有就执行React.createElement创建component,否则进入下一条
          3. 判断当前Route是否有render参数(函数),有就执行render函数,否则进入下一条。
          4. 返回null
          1. 返回null

当我们的路由发生变化时,Router中所监听的history函数将会触发,返回新的location对象,这是将会触发Router的setState,然后对应所有绑定Router的Route都将会重新渲染判断是否命中路由来进行重新渲染。

Switch组件

如果我们只是单纯的使用Route组件来设置路由,当我们的当前的url满足多条路由规则的情况下,会出现多个Route的子组件进行渲染,这个时候如果当我们使用Switch包裹多个Route组件的话,那么只会渲染首先命中当前url的Route组件,具体是如何实现的呢?

所以Switch和Route的区别是在于,Switch只会渲染满足的条件的Route,而Route会根据传入的path来判断如果满足当前url的情况下,就会渲染Route的子组件。Switch就是从而实现Route同时只会命中一个的功能。

Link组件

Link组件也是相当简单的一个组件,内部主要做了以下事情:

  1. 判断传入参数replace,是使用replace还是push进行跳转
  2. 执行传入的onClick事件
  3. 判断一些参数,例如(传入_blank参数,将交由浏览器处理)
  4. 触发内部点击事件,使用history库实例后的push或replace来控制前端路由跳转
  5. 禁止默认事件

以下是Link组件的点击处理逻辑:

Link组件是如何获取到history的那,我们使用的时候并没有传递进去当前的history实例呀,实际上还记得之前看Route组件的时候,在return的时候,又包裹了一层Context吗,其实实际上就是给Link这类型的标签方便获取到history实例的,而Link组件也是使用Context。

结语

React Router的代码其实很好理解,主要涉及到的是history这个库是核心点,整个路由的触发事件的封装,抹平了浏览器差异。其次就是React Router实际是基于context来实现Router、Route、Link等组件中,history,location等值的传递。

0 人点赞