react-router6篇

327 阅读4分钟

流程图

路由原理

  • 有两种实现方法
    • hashRouter 利用hash实现路由切换
    • BrowserRouter 实现h5Api实现路由切换
  • 相同点
    • 都是创建一个ref对象 使用History库来创建 createHashHistory 对象和 createBrowserHistory对象 可以获取到history
    • 存储history的action和location 监听history变化 改变存储状态内的location的值
    • 返回一个Router对象 children 子元素 location是当前场景 navigationType 导航状态 navigator 导航(历史对象)
  • 不同点
    • BrowserRouter是根据H5(window.history)提供的Api来生成的
    • hashRouter是自己模拟浏览器历史对象 根据hash的pushState来压入历史对象 监听window.onpopstate来监听变化来监听

createHashHistory的实现

  • 创建一个历史栈 用来模拟浏览器历史栈 historyStack
  • 定义历史栈默认指针 index
  • 定义默认动作 action
  • 定义当前路径状态 state
  • 定义监听函数的数组 listeners
  • 定义一个监听函数 返回一个取消监听的函数 listen
  • go函数 改变action 从历史栈中找到对应的location对象 赋值给widnow.location
  • goBack go(-1)
  • goForword go(1)
  • push方法 改变action 兼容pathname的对象写法 把pathname赋值给window.location.hash
  • 监听hashchange事件 获取当前路由 添加到history对象中 并派发listeners的所有对象执行

createBrowserHistory的实现

  • 使用window自带的history对象
  • 定义监听函数的数组 listeners
  • 定义一个监听函数 返回一个取消监听的函数 listen
  • 监听popState事件
  • push 是调用history的pushState
  • notify 获取最新状态 添加到history对象中 并派发listeners的所有对象执行

HashRouter的实现

  • 创建一个ref对象
  • 给ref.current绑定上createHashHistory实现的history
  • 定义一个history的useState 方便history数据更新的时候更新视图
  • useLayoutEffect来监听history.listen函数 传入history.location和history.action 然后setState
  • 返回一个Router组件

Router组件

  • 获取导航上下文的内容
  • 获取路径上下文的内容
  • 并把Router的子节点放到路径上下文的子节点内

Routes

  • 创建一个子节点的路由表 生成路由表
[{
    path:'/home',
    element:element节点
}]
  • 把这个路由表传入useRoutes 根据路由表渲染真正的组件

useRoutes

  • 作用 可以直接支持路由表的使用 渲染组件
  1. 获取当前的路由对象 useLocation
  2. 获取当前的路径字符串
  3. 用当前的地址栏中的路径和路由进行匹配
    • 打平分支
      • 循环路由表
      • 定义一个路由元数据 mate
      • 构建子节点完整路径
      • 在父meta中增加自己的mate routesMeta
      • 递归构建路由
      • 插入一个分数的概念 用来后续的排序
        • 路由带* -2分
        • 路由有index 2分
        • 动态变量 :id 3分
        • 空分数 1分
        • 静态分数 10分
        • 初始化分数 路径分割成数组的长度
      • 生成一个branches
            {
            path,  // 完整的路径
            routesMeta, // 路由元数组的集合
            score: computeScore(path, route.index) // 排序分数
            }
        
    • 根据分数进行路由排序
      • 如果分数一样,按照分数倒序排序
      • 如果分数不一样 就对比索引
        • 如果级别数量相等,并且父亲都 一样,说是他们是兄弟
        • 如果是兄弟的话,那和比索引,索引越小级别越高,索引越大,级别越低
        • 如果不是兄弟,那就认为相等的
    • 按照排序完的顺序依次进行匹配 如果匹配上了 直接退出循环,不再进行后续匹配
      • 拿到当前地址栏的路径 根据/分割 跟routesMeta的路径进行分段匹配
      • 匹配成功了就把params追加上
      • 把所有匹配上的路由返回去 如果是二级路由 其实第一级路由也会返回 所以最后要用reduceRight来输出
  4. 渲染匹配的节点
    • 渲染结果是从右到左的 是因为左边的是父级路由 最右边的是要渲染的节点
    • 给路由上下文中增加outlet matchers

useLocation

  • 返回路径上下文中value的location

Route 是一个空对象的语法糖

useNavigate

  • 获取导航上下文对象中的navigator 也就是history 调用push方法即可
  • 因为用了useMemo 所以调用的时候用了useCallback
  • 返回useCallback(to)

Navigate

  • 在useLayoutEffect中使用useNavigate

useParams

  • 获取路由上下文中的matches
  • 获取最后一项routeMatch
  • 如果存在 就返回这个的params 如果没有就返回一个空对象

outlet

  • 返回路由上下文对象中的outlet

link

  • 调用useNavigate 获取history
  • 定义一个a标签 调用history.to方法 并把入参传入进行路由跳转

Navlink

  • 在link的基础上加了一层判断
  • 可传入className和style
  • 要兼容className和style的函数写法
  • 定义一个变量 如果目标路径和当前路径相同 或者是 当前路径的子路径且没有end结束符 为true 作为入参传到到className的函数体内