react-router v6 和 history v5 源码简析

382 阅读3分钟

react-router-dom v6

基于 React 编写的 React 前端路由库。

BrowserRouter

调用 createBrowserHistory 创建了一个 history 对象,访问 history.action、history.location 属性,将 location、action 作为 props 传递给 Router 组件渲染对应路由。同时通过 history.listen 监听 history 对象的改变,一旦发生改变,就通过 setState 修改 loactionaction 的值。从而导致 children 重新渲染。

HashRouter

调用 createHashHistory 创建了一个 history 对象,然后流程和 BrowserRouter 一样。

Link

渲染一个 a 标签,通过 useHref 获取到完整路径,通过 useLinkClickHandler 返回的函数进行路由跳转同时会阻止浏览器默认行为。

如果对在 Link 的 onClick 事件的函数中执行了 event.preventDefault(),点击 Link 后,路由将不会改变。

如果 LinkreloadDocumentture,点击则会刷新页面。Link 内部 useLinkClickHandler 返回的函数依旧不会执行。路由能够跳转是因为重新向服务器请求了对应 url 的资源。

react-router

Router

通过 basenamepathname 来判断顶级路由是否匹配,来决定是否渲染 children

匹配后使用 NavigationContextLocationContext 来向下传递状态,让需要的组件自己订阅对应的状态。

Routes

使用 createRoutesFromChildren 根据 Routes 组件的 children 创建一个 route 配置对象数组。然后再把配置对象数组传入 useRoutes 经过路由匹配和排序后返回一个 React.ReactElement 元素。

matches.reduceRight((outlet, match, index) => {
  return (
    <RouteContext.Provider
      children={
        match.route.element !== undefined ? match.route.element : outlet
      }
      value={{
        outlet,
        matches: parentMatches.concat(matches.slice(0, index + 1)),
      }}
    />
  );
}, null as React.ReactElement | null);

//上面的代码形成一个匹配路由的嵌套树。在我们编写的组件中使用 <Outlet/> 时,就是通过 useContext 访问了上面代码中对应嵌套层次的 outlet,才能在父路由组件中渲染子路由组件。

匹配和排序路由比较复杂,有兴趣的可以自己去看源码

Route

该组件的目的只是为了声明路由和对应的组件。

该组件必须被包裹在 Routes 组件内部,不能直接渲染,否则会抛出异常。因为该组件内部只有一段抛出异常的代码,避免没有被 Routes 包裹直接渲染。

history v5

使用 JavaScript 来管理会话和历史记录,同时屏蔽了不同平台和不同的路由模式的差异。

createHashHistory

通过执行 createHashHistory(),创建了一个 hashHistory 对象,提供了 push、replace、listen等方法,通过 listen 方法可以添加订阅函数。

监听路由变化

1、通过监听 hashchange、popstate 事件,在事件回调函数中执行通过 listen 方法添加的订阅函数。将路由跳转后的 actionlocation作为参数传递到订阅函数中。

2、提供 push、replace 等方法,在 push、replace 内部通过 history.pushState(方法执行失败时会通过 window.location.assign(url) 修改 url)history.repalceState 方法修改 url。然后执行通过 listen 方法添加的订阅函数,将路由跳转后的 actionlocation作为参数传递到订阅函数中。

注意

当通过 window.location.hash 修改 url 的 hash 时,会触发 popstatehashchange 事件,先触发 popstate 事件,再触发 hashchange 事件(在该事件回调函数中会判断 url 是否相等,避免重复执行)。

createBrowserHistory

通过执行 createBrowserHistory(),创建了一个 browserHistory 对象,提供了 push、replace、listen等方法,通过 listen 方法可以添加订阅函数。

监听路由变化

1、通过监听 popstate 事件,在事件回调函数中执行通过 listen 方法添加的订阅函数。将路由跳转后的 actionlocation作为参数传递到订阅函数中。

2、提供 push、replace 等方法,在 push、replace 内部通过 history.pushState(方法执行失败时会通过 window.location.assign(url) 修改 url)history.repalceState 方法修改 url。然后执行通过 listen 方法添加的订阅函数,将路由跳转后的 actionlocation作为参数传递到订阅函数中。