react-react router原理

loading 2023年01月06日 193次浏览

该博客主要归纳于这里

1. 三个库

(1) history
整个路由原理的核心,里面集成了popState,pushState等等底层路由实现的原理方法。

(2) react-router
react-router-dom的核心,里面封装了Router,Route,Switch等核心组件,实现了从路由的改变到组件的更新的核心功能

(3) react-router-dom
在react-router的核心基础上,添加了用于跳转的Link组件,和history模式下的BrowserRouter和hash模式下的HashRouter组件等。

所谓BrowserRouter和HashRouter,也只不过用了history库中createBrowserHistory和createHashHistory方法

2. 单页面路由

单页面应用路由实现原理是:切换url,监听url变化,从而渲染不同的页面组件。

主要的方式有history模式和hash模式

2.1 history模式

2.1.1 改变路由

history.pushState()方法用于在历史中添加一条记录。
添加新记录后,浏览器地址栏立刻显示新地址,但并不会跳转,甚至也不会检查地址是否存在,它只是成为浏览历史中的最新记录。

history.pushState(state,title,path)
  • state:一个与指定网址相关的状态对象,popstate事件触发时,该对象会传入回调函数。如果不需要可填null。
  • title:新页面的标题,但是所有浏览器目前都忽略这个值,可填null。
  • path:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个地址。

history.replaceState()方法用来修改history对象的当前记录,其他都与pushState()方法一模一样。

history.replaceState(state,title,path)

2.1.2 监听路由

history对象出现变化时,就会触发popstate事件。

用 history.pushState() 或者 history.replaceState() 不会触发 popstate 事件。popstate事件只会在浏览器某些行为下触发, 比如点击后退、前进按钮或者调用history.back()、history.forward()、history.go()方法。

window.addEventListener('popstate',function(e){
    /* 监听改变 */
})

2.2 hash模式

2.2.1 改变路由

window.location.hash

通过window.location.hash 属性获取和设置 hash 值。

2.2.2 监听路由

onhashchange

window.addEventListener('hashchange',function(e){
    /* 监听改变 */
})

3. history库

react-router路由离不开history库,history专注于记录路由history状态,以及path改变了,我们应该做什么。

在history模式下用popstate监听路由变化,在hash模式下用hashchange监听路由的变化。

接下来我们看Browser模式下的createBrowserHistory和Hash模式下的createHashHistory方法。

3.1 createBrowserHistory

Browser模式下路由的运行,一切都从createBrowserHistory开始。

这里就重点分析一下history.push方法

 const push = (path, state) => {
    const action = 'PUSH'
    /* 1 创建location对象 */
    const location = createLocation(path, state, createKey(), history.location)
    /* 确定是否能进行路由转换,还在确认的时候又开始了另一个转变 ,可能会造成异常 */
    transitionManager.confirmTransitionTo(location, action, getUserConfirmation, (ok) => {
      if (!ok)
        return
      const href = createHref(location)
      const { key, state } = location
      if (canUseHistory) {
        /* 改变 url */
        globalHistory.pushState({ key, state }, null, href)
        if (forceRefresh) {
          window.location.href = href
        } else {
          /* 改变 react-router location对象, 创建更新环境 */
          setState({ action, location })
        }
      } else {
        window.location.href = href
      }
    })
  }

history.push流程大致是首先生成一个最新的location对象,然后通过history.pushState改变浏览器当前的path,最后通过setState方法通知React-Router更新,并传递当前的location对象,由于这次url变化是history.pushState导致的,并不会触发popState方法,所以需要手动setState,触发组件更新。

3.2 createHashHistory

hash模式下和history模式下主要区别在于监听路由,push方法和replace方法。

监听路由通过hashchange,区别于history通过popstate。

在hash模式下 ,history.push底层是调用了window.location.href来改变路由。history模式下是通过pushstate。
history.replace底层是调用window.location.replace改变路由。history模式下是通过replacestate。

/* 对应 push 方法 */
const pushHashPath = (path) =>
  window.location.hash = path

/* 对应replace方法 */
const replaceHashPath = (path) => {
  const hashIndex = window.location.href.indexOf('#')

  window.location.replace(
    window.location.href.slice(0, hashIndex >= 0 ? hashIndex : 0) + '#' + path
  )
}

4. 流程分析

当地址栏的URL发生改变,组件的更新渲染都经历了什么?

以history模式进行参考:

  • url改变,调用事件监听方法popstate(),触发回调函数handlePopState()
  • 触发history下的setstate方法,产生新的location对象
  • 通知Router组件更新location并通过context传递
  • Switch根据传递的更新流,匹配出符合的Route组件
  • 最后由Route组件取出context内容,传递给渲染页面后渲染更新

当调用history.push()方法,切换路由,组件的更新渲染经历了什么?

还是以history模式进行参考:

  • 首先调用history的push方法,通过history.pushState改变URL
  • 剩下四步同上,也就是只有第一步不一样

5. 总结

history库提供了核心api,比如监听路由,更改路由的方法,以及保存路由状态state

react-router库提供路由渲染组件,路由唯一性匹配组件,重定向组件等功能组件