欢迎光临散文网 会员登陆 & 注册

React-router-6原理剖析

2023-02-27 17:42 作者:TongMarsh  | 我要投稿

基本使用

从react-router-dom导出BrowserRouter放在最外层,使用Routes和Route注册路由,Routes组件会根据当前location匹配路由,渲染对应组件。支持嵌套路由,也就是一个路由允许有children,最终形成一个路由tree。当location匹配到一个路由时,会渲染对应路径上的组件。

Router object

将声明式的路由改写成object形式的路由,子路由放在父路由的children字段内,使用导出的useRoutes hook得到需要渲染组件。

原理

需求分析

router需要提供的功能是:

  1. 提供注册路由的方式,用户指定path以及对应的组件,

  2. 根据浏览器的location能够匹配path,确定组件,然后渲染

  3. 需要支持嵌套路由,例如,如前文的路由信息,当location匹配到/about时,需要渲染的组件时Layout和About,而About是嵌套路由对应的组件,是在Layout组件的outlet里渲染

  4. 监听浏览器跳转,刷新

代码结构

React-router 6是使用yarn workspace组织monorepo,包含了react-router,react-router-dom, react-router-dom-v5-compat, react-router-native。先只考虑web端的。

react-router实现了router大部分的功能,包括Router,MemoryRouter,Routes,Route,useRoutes.....

react-router-dom实现了少数web端需要的功能,包括BrowserRouter, HashRouter, Link, NavLink,useLinkClickHandler, useSearchParams。

react-router-dom导入了react-router所有的导出,然后重新导出了这些变量。(re-export all the import from react-router)

路由注册

react-router主打声明式路由,jsx书写路由规则是最基本的方式。

例如

涉及到的组件是Routes和Route,Route组件是一个用来标识的函数,内部没有任何逻辑,也不会被渲染,唯一的用处是承载路由相关的信息,让Routes组件来收集起来使用。

Routes组件的功能是收集Route的路由信息,根据location匹配组件渲染。内部使用了useRoutes进行路由匹配返回最终需要渲染的结果。所以如果直接提供router object,可以跳过Routes内部收集重构路由对象的步骤,直接useRoutes获取匹配结果。

react运行时,Routes组件会以children的形式得到路由信息,如下图

遍历children收集路由信息时,可以从child里看出是否是Route组件(根据type属性),从props属性里获取Route上书写的path和element等匹配规则。这一步的目的就是得到前文Router object示例中的routes对象,即path及其对应的element,以及它的子路由信息children。

Routes本质上是一个React组件,它的功能就是根据路由配置信息匹配当前location,找到需要渲染的组件,然后渲染出来。只不过因为声明式路由,所以多了重构出router object这一步。

匹配渲染

匹配的参数分别是location和router object。react-router内部使用了history包,在web端是使用该包创建了browserHistory对象,获取到location信息作为当前页面的路径信息,而将browserHistory对象作为操作浏览器history的对象(在最外层的Router里provide)。

总之,可以认为已经有了当前路径pathname和路由配置router object。目标是匹配出需要渲染的组件。

react-router分为四步:

  1. 打平flatten

  2. 排序

  3. 匹配

  4. 渲染

打平

入参是router object

结果

打平的目的是将嵌套的router object转换成一层的数组,同时对每一条route信息做了一些转换。包括:将子路由path和parent连接起来得到完整的path,计算了一个score,将父路由route和子路由route都放到一个routesMeta里,也就是routesMeta里存储了从根到叶子结果完整路径的路由信息。

排序

将完整path按分隔符'/'拆开后,计算得分规则:

  1. 包含通配符*,扣2分

  2. 路径有index标记,加2分

  3. 动态param,3分 (如 /:id, id为动态的)

  4. 空字符, 1分

  5. 静态字符串,10分

根据score从高到低排序

匹配

正则匹配先跳过

渲染

下图是当前pathname为‘/nothing-here’,最终匹配得到的routesMeta的结构。

第一个对应的是根路由的Layout组件,第二个是子路由NoMatch组件。需要达到的效果是将NoMatch组件正确渲染到Layout组件里Outlet的地方。

也就是最终效果是像下面的jsx一样的,子路由的element是作为provider里的值传递给Layout中的Outlet使用的,Outlet内部获取到上层的outlet值直接渲染,就成功将子路由的element在父路由的element内部渲染出来了。

所以最后渲染是倒着遍历数组。

对子路由,创建如下的元素,

对父路由

每一步遍历要做的是渲染当前的element,为了正确渲染,需要在element外面Provider两个值。而上一步子路由的结果又作为下一步的outlet的值,从而完成了outlet的功能。



React-router-6原理剖析的评论 (共 条)

分享到微博请遵守国家法律