IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> react-router v6 -> 正文阅读

[JavaScript知识库]react-router v6

文章简介

本文主要介绍:

新版本路由和老版本的差异,使用区别,API 区别。
新版本路由组件 Router ,Routes ,和 Route 的原理。
Outlet 组件原理。
useRoutes 原理。

目录

一,react路由6和路由5的差异

1 路由5中的Switch与路由6中的Routes

2 嵌套路由的升级

3 路由状态和页面跳转

状态获取

路由跳转

动态路由

url参数信息获取

官方文档

二 原理分析

1 新版 Route 设计

2 外层容器,更新源泉 BrowserRouter | HashRouter | Router

老版本的 BrowserRouter

新版本的 BrowserRouter

新版本 Router

3 原理深入,Routes 和 branch 概念

Routes 和 useRoutes

useRoute

路由状态传递

三 v5 和 v6 区别总结

组件层面上:

使用层面上:

原理层面上:



一,react路由6和路由5的差异

?

1 路由5中的Switch与路由6中的Routes

新版本的 router 没有?Switch?组件,取而代之的是 Routes ,但是在功能上?Routes?是核心的,起到了不可或缺的作用。老版本的 route 可以独立使用,新版本的 route 必须配合 Routes 使用。

在新版的 router 中,已经没有匹配唯一路由的?Switch?组件,取而代之的是?Routes?组件,但是我们不能把 Routes 作为 Switch 的代替品。

因为在新的架构中 ,Routes 充当了很重要的角色,在?react-router路由原理?文章中,曾介绍到 Switch 可以根据当前的路由 path ,匹配唯一的 Route 组件加以渲染。

但是 Switch 本身是可以被丢弃不用的,但是在新版的路由中, Routes 充当了举足轻重的作用。

比如在 v5 中可以不用 Switch 直接用 Route,但是在 v6 中使用 Route ,外层必须加上 Routes 组件,也就是 Routes -> Route 的组合。

如果 Route 外层没有 Routes ,会报出错误。

2 嵌套路由的升级

新版本路由引入 Outlet 占位功能,可以更方便的配置路由结构,不需要像老版本路由那样,子路由配置在具体的业务组件中,这样更加清晰,灵活。

对于新版本的路由,嵌套路由结构会更加清晰.

比如在老版本的路由中,配置二级路由,需要在业务组件中配置,我们需要在?Children?组件中进行二级路由的配置。

但是在 路由6 中,对于配置子路由进行了提升,可以在子路由直接写在 父组件里。

Container 内部运用了 v6 Router 中的?Outlet?。

而 Outlet 才是真正渲染子路由的地方。

这里的 Outlet 更像是一张身份卡,证明了这个就是真正的路由组件要挂载的地方,而且不受到组件层级的影响。

这种方式更加清晰,灵活,能够把组件渲染到子组件树的任何节点上。

3 路由状态和页面跳转

状态获取

对于路由状态 location 的获取 ,可以用自定义 hooks 中?useLocation?。location 里面保存了 hash | key | pathname | search | state 等状态。

路由跳转

新版路由提供了?useNavigate?,实现路由的跳转。

动态路由

新版路由里面实现动态路由,也变得很灵活,可以通过 useParams 来获取 url 上的动态路由信息。比如如下

url参数信息获取

新版路由提供?useSearchParams?可以获取?|?设置?url 参数

官方文档

v6 还提供了一些其他功能的 hooks ,这里就不一一讲了,有兴趣的同学可以看一下官方文档,

Docs Home v6.3.0 | React Router

二 原理分析

上述介绍了从使用上,v5 和 v6 版本路由的区别。接下来,我们重点看一下新版 Route 的原理。以及和老版本有什么区别。

1 新版 Route 设计

老版本的路由,核心的组件是 **Route**,之前的路由原理文章中介绍过,Route 内部通过消费 context 方式,当路由改变的时候,消费 context 的 Route 会重新渲染,内部通过 match 匹配到当前的路由组件是否挂载,那么就是说真正去匹配,去挂载的核心组件为 Route。

而在新版本的 Route 中,对于路由更新,到路由匹配,再到渲染真正的页面组件,这些逻辑主要交给了 Routes ,而且加了一个?branch?‘分支’ 的感念。可以把新版本的路由结构理解一颗分层级的树状结构,也就是当路由变化的时候,会在 Routes 会从路由结构树中,找到需要渲染 branch 分支。此时的 Route 组件的主要目的仅仅是形成这个路由树结构中的每一个节点,但是没有真正的去渲染页面。

新版本的路由可以说把路由从业务组件中解耦出来,路由的配置不在需要制定的业务组件内部,而是通过外层路由结构树统一处理。对于视图则是通过?OutletContext?来逐层传递,接下来我们一起来看一下细节。

2 外层容器,更新源泉 BrowserRouter | HashRouter | Router

老版本的 BrowserRouter

import { createBrowserHistory as createHistory } from "history";
class BrowserRouter extends React.Component {
  history = createHistory(this.props) 
  render() {
    return <Router history={this.history} children={this.props.children} />;
  }
}
  • 老版本的 BrowserRouter 就是通过?createHistory?创建?history?对象,然后传递给 Router 组件。

新版本的 BrowserRouter

export function BrowserRouter({
  basename,
  children,
  window
}: BrowserRouterProps) {
  /* 通过 useRef 保存 history 对象  */
  let historyRef = React.useRef<BrowserHistory>();
  if (historyRef.current == null) {
    historyRef.current = createBrowserHistory({ window });
  }

  let history = historyRef.current;
  let [state, setState] = React.useState({
    action: history.action,
    location: history.location
  });
  /* history 变化,通知更新。 */
  React.useLayoutEffect(() => history.listen(setState), [history]);

  return (
    <Router
      basename={basename}
      children={children}
      location={state.location}
      navigationType={state.action}
      navigator={history}
    />
  );
}

新版本的 BrowserRouter 的功能如下:

  • 通过?createBrowserHistory?创建?history?对象,并通过?useRef?保存 history 对象。
  • 通过?useLayoutEffect?来监听?history?变化,当 history 发生变化(浏览器人为输入,获取 a 标签跳转,api 跳转等 )。派发更新,渲染整个 router 树。这是和老版本的区别,老版本里面,监听路由变化更新组件是在 Router 中进行的。
  • 还有一点注意的事,在老版本中,有一个?history?对象的概念,新版本中把它叫做?navigator?。

新版本 Router

function Router({basename,children,location:locationProp,navigator}){
  /* 形成 navigationContext 对象   保存 basename , navigator 对象等信息。*/
  let navigationContext = React.useMemo(
    () => ({ basename, navigator, static: staticProp }),
    [basename, navigator, staticProp]
  );
  /* 把 location 里面的状态结构出来  */
  const { pathname, search, hash, state, key } = locationProp
  /* 形成 locationContext 对象,保存 pathname,state 等信息。 */
  let location = React.useMemo(() => {
    /* .... */
     return { pathname, search, hash, state, key  }
  },[basename, pathname, search, hash, state, key])
  /* 通过 context 分别传递 navigationContext 和 locationContext */
   return (
    <NavigationContext.Provider value={navigationContext}>
      <LocationContext.Provider
        children={children}
        value={{ location, navigationType }}
      />
    </NavigationContext.Provider>
  )
}

Router?在新版路由中充当的角色如下:

  • 通过 useMemo 来派生出负责跳转路由等功能的 navigator 对象和路由信息的 location 对象。通过 React context 来传递它们。
  • 当路由变化时候,在?BrowserRouter?中通过 useState 改变 location ,那么当 location 变化的时候,LocationContext?发生变化,消费 LocationContext 会更新。

3 原理深入,Routes 和 branch 概念

Routes 和 useRoutes

首先来看一下?Routes?的实现

export function Routes({children,location }) {
  return useRoutes(createRoutesFromChildren(children), location);
}

使用?<Routes />?的时候,本质上是通过 useRoutes 返回的 react element 对象,那么可以理解成此时的 useRoutes 作为一个视图层面意义上的?hooks?。 Routes 本质上就是使用 useRoutes 。

useRoute

function useRoutes(routes, locationArg) {

    let locationFromContext = useLocation();
   /* TODO: 第一阶段:计算 pathname  */
   // ...代码省略

   /* TODO: 第二阶段:找到匹配的路由分支  */
  let matches = matchRoutes(routes, {
    pathname: remainingPathname
  });
  console.log('----match-----',matches)

  /* TODO: 第三阶段:渲染对应的路由组件 */
  return _renderMatches(matches && matches.map(match => Object.assign({}, match, {
    params: Object.assign({}, parentParams, match.params),
    pathname: joinPaths([parentPathnameBase, match.pathname]),
    pathnameBase: match.pathnameBase === "/" ? parentPathnameBase : joinPaths([parentPathnameBase, match.pathnameBase])
  })), parentMatches);
}

路由状态传递

首先我们知道 reduceRight 是从右向左开始遍历,那么之前讲到过 match 结构是 root -> children -> child1, reduceRight 把前一项返回的内容作为后一项的 outlet,那么如上的 match 结构会这样被处理。

  • 1 首先通过 provider 包裹 child1,那么 child1 真正需要渲染的内容 Child1 组件 ,将被当作 provider 的 children,最后把当前 provider 返回,child1 没有子路由,所以第一层 outlet 为 null。
  • 2 接下来第一层返回的 provider,讲作为第二层的 outlet ,通过第二层的 provider 的 value 里面 outlet 属性传递下去。然后把 Layout 组件作为 children 返回。
  • 3 接下来渲染的是第一层的 Provider ,所以 Layout 会被渲染,那么 Child1 并没有直接渲染,而是作为 provider 的属性传递下去。

三 v5 和 v6 区别总结

组件层面上:

  • 老版本路由采用了 Router Switch Route 结构,Router -> 传递状态,负责派发更新; Switch -> 匹配唯一路由 ;Route -> 真实渲染路由组件。
  • 新版本路由采用了 Router Routes Route 结构,Router 为了抽离一 context; Routes -> 形成路由渲染分支,渲染路由;Route 并非渲染真实路由,而是形成路由分支结构。

使用层面上:

  • 老版本路由,对于嵌套路由,配置二级路由,需要写在具体的业务组件中。
  • 新版本路由,在外层统一配置路由结构,让路由结构更清晰,通过 Outlet 来实现子代路由的渲染,一定程度上有点类似于 vue 中的?view-router
  • 新版本做了 API 的大调整,比如 useHistory 变成了 useNavigate,减少了一些 API ,增加了一些新的 api 。

原理层面上:

  • 老版本的路由本质在于 Route 组件,当路由上下文 context 改变的时候,Route 组件重新渲染,然后通过匹配来确定业务组件是否渲染。
  • 新版本的路由本质在于 Routes 组件,当 location 上下文改变的时候,Routes 重新渲染,重新形成渲染分支,然后通过 provider 方式逐层传递 Outlet,进行匹配渲染。
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-09-25 23:09:09  更:2022-09-25 23:10:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 10:06:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码