React-router源码—好家伙!

本篇文章参考以下博文

《图解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/> } />

  路由的基本功能,错略介绍了下,具体细节大家可以自己查看源码,继续学习。




猜你喜欢

转载自blog.csdn.net/EcbJS/article/details/115334887