React-router源码—好家伙!

本文详细解读React Router的内部机制,涉及url监听的不同方法、路径匹配规则及组件渲染方式,带你走进React路由实现的奥秘。

本篇文章参考以下博文

《图解React-router源码》–魔术师卡颂

前言

  最近在与前辈的交流过程中,大佬抛出来一个问题, react 路由是如何实现的?平时在学习过程中,更多的是使用,还没有研究过这东西的工作原理,今天我们就来一起挖一挖,这东西是怎么做出来的。

路由原理

  首先我们需要先有一个概念,那就是路由是怎么实现的?原理呢其实就是页面的 url 发生变化,然后去匹配路径,渲染对应组件。

  接下来如何实现?

实现步骤

  总的实现步骤可以分为3步:

  1. 如何监听 url 变化?
  2. 如何匹配 path 路径,匹配规则是什么?
  3. 如何渲染对应组件。

  核心包有两个 react-router ,reat-router-dom

  其中 react-router-dom 是在浏览器中使用的包,包含四个组件, BrowserRouter、Link、NavLink、HashRouter 。其他的都是引用 react-router

  下面举一个常用例子来看。

export default function App() {
   return (
       <BrowserRouter>
           <div>
               <Link to="/">Home</Link>
               <Link to="/about">About</Link>
               <Link to="/more">More</Link>
               <Switch>
                   <Route path="/about">
                       <About />
                   </Route>
                   <Route path="/">
                       <Home />
                   </Route>
               </Switch>
           </div>
       </BrowserRouter>
   );
}

  观察上面的代码,我们可以总结以下几点:

  1. 组件是被包裹在 <BrowserRouter> 标签里,这么做的意义是什么?
  2. <Switch> 标签作用是只匹配一个 path 路径,如何实现?
  3. <Route> 的属性中有 path 路径,内部是需要展示的组件。

  我们先通过一张图来理解以下 react-router 的流程。
在这里插入图片描述

一、监听 url 变化

  正常情况下,当 url 发生变化时,浏览器会向服务器发送请求,但是使用以下两种方法不会向服务器发送请求。

  • 基于 hash
  • 基于 history

1. 选择方式: history 或 hash

   HashRouter 先是从 history 中引用 createBrowserHistory ,然后将 history children 传入到 Router BrowseHistory 同理。
在这里插入图片描述

  注意: BrowseHistory 必须依赖服务器让 url 都映射到 index.html ,否则会 404

2. 监听 URL 的变化

  拿到对应的 history,location,match 等通过 Provider 注入到子组件中
在这里插入图片描述

二、Route 中匹配渲染组件

在这里插入图片描述
(由于 return 里面不能写 if else ,三元运算符叠加起来使用,阅读是有点麻烦)

  这代码可以分两部分理解:

  1. 是否匹配
  2. 渲染组件

1. 是否匹配

   computedMatch 是使用 Switch 包裹的子组件才有的值, Switch 的作用是从上到下开始渲染,只要匹配到一个,其他的就不匹配。所以这里会先判断 computedMatch
在这里插入图片描述

  匹配解析 path ,这里使用了第三方库 path-to-regexp
在这里插入图片描述
在这里插入图片描述
  解析规则如下:

const fn = match("/user/:id", { decode: decodeURIComponent });

fn("/user/123"); //=> { path: '/user/123', index: 0, params: { id: '123' } }
fn("/invalid"); //=> false
fn("/user/caf%C3%A9"); //=> { path: '/user/caf%C3%A9', index: 0, params: { id: 'café' } }

2. 组件渲染方式

  文档中支持三种渲染方式,分别是:

// children 方式
<Route exact path="/">
   <HomePage />
</Route>

// func 方式
<Route
   path="/blog/:slug"
   render={({ match }) => {
     // Do whatever you want with the match...
     return <div />;
   }}
/>

// component 方式
<Route path="/user/:username" component={User} />

  源码部分显示如下:
在这里插入图片描述
  这部分有点乱,需要结合流程图理解。
在这里插入图片描述
  从上面的代码我们可以看出:

  1. Router 渲染的优先级: children > component > render ,三种方式互斥,只能使用一种。
  2. 不匹配的情况下,只要 children 是函数,也会渲染
  3. component 是使用 createComponent 来创建的, 这会导致不再更新现有组件,而是直接卸载再去挂载一个新的组件。如果是使用匿名函数来传入 component ,每次 render 的时候,这个 props 都不同,会导致重新渲染挂载组件,导致性能特别差。因此,当使用匿名函数的渲染时,请使用 render children
// 不要这么使用
<Route path="/user/:username" component={() => <User/> } />

  路由的基本功能,粗略介绍了下,具体还有很多细节优化的地方大家可以自己查看源码,继续学习。




### 解决方案 在处理 `npm ERESOLVE unable to resolve dependency tree` 错误时,特别是针对 React 版本与 `react-router` 和 `react-router-config` 不兼容的情况,可以采取以下方法来解决问题。 #### 方法一:强制忽略 peerDependencies 冲突 可以通过添加参数 `--legacy-peer-deps` 来跳过 peerDependency 的校验。这种方法适用于某些情况下不需要严格遵循依赖关系的场景[^1]: ```bash npm install --legacy-peer-deps ``` 此命令会让 npm 使用旧版的行为逻辑,忽略掉无法解析的 peerDependency 冲突问题。 --- #### 方法二:降级或升级相关依赖 如果希望彻底解决版本冲突问题,则需要调整依赖库的版本以满足其相互之间的兼容性要求。以下是具体的分析和操作步骤: 1. **React 16.x 与 `react-router@7.x` 的不兼容** - 根据官方文档,`react-router@7.x` 要求最低支持 React 17 或更高版本[^2]。 - 如果当前项目使用的 React 是 16.13.1,则建议将 `react-router` 下降到与其兼容的版本(如 `react-router@5.x` 或 `react-router@6.x`),或者升级 React 到 17/18 并保持使用 `react-router@7.x`。 2. **`react-router-config` 的适配** - `react-router-config` 主要用于静态路由配置,在现代应用中已逐渐被动态路由替代。它通常与较老版本的 `react-router` 配合使用。 - 建议检查项目的实际需求,如果不再需要 `react-router-config`,可以直接移除该依赖;否则需确保它的版本与所选的 `react-router` 兼容。 更新后的 `package.json` 示例如下: - 升级到 React 18,并匹配合适的 `react-router` 版本: ```json { "dependencies": { "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.27.0" } } ``` - 若保留 React 16,则选择较低版本的 `react-router`: ```json { "dependencies": { "react": "^16.13.1", "react-dom": "^16.13.1", "react-router-dom": "^5.3.4" } } ``` 执行安装命令后验证是否解决了冲突问题: ```bash npm install ``` --- #### 方法三:手动编辑 package-lock.json 文件 有时即使指定了正确的版本范围,仍可能因缓存或其他因素导致错误持续发生。此时可尝试删除现有的锁文件并重新生成: ```bash rm -rf node_modules package-lock.json npm cache clean --force npm install ``` --- #### 方法四:切换至 Yarn 工具 Yarn 提供更灵活的依赖解析机制,能够更好地应对复杂的依赖树结构。对于难以解决的冲突情况,推荐改用 Yarn 进行依赖管理[^3]: ```bash yarn add react@16.13.1 react-dom@16.13.1 react-router-dom@5.3.4 ``` --- ### 总结 以上提供了四种不同的解决方案,分别对应不同层次的需求和技术背景。优先考虑通过 `--legacy-peer-deps` 参数快速修复临时问题,而长期来看则应优化依赖版本组合以减少潜在风险。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值