图解 React-router 源码

大家好,我是若川。欢迎加我微信 ruochuan12今天分享一篇react-router源码文章,深入理解路由的本质。微信预计阅读只需8分钟。点击下方卡片关注我,或者查看源码系列文章。


阅读源码小 tips:从整体到细节,刚开始不要太拘泥于一行代码的实现,先从整体去理解,理解好各自的关系,再去读源码。

首先,我们先不纠结于源码细节。先用最简单的话来概括一下 React-router 到底做了什么?

本质上, React-Router 就是在页面 URL 发生变化的时候,通过我们写的 path 去匹配,然后渲染对应的组件。

那么,从这句话,我们想一下如何分步骤实现:

  1. 如何监听 url 的变化 ?

  2. 如何匹配 path,按什么规则 ?

  3. 渲染对应的组件

了解好需要实现的关键步骤,我们来将仓库源码下载下来。

接下来我们看一下 GitHub, 它使用 lerna 管理同时管理多个包.也就是 Multirepo 概念。

react-router 使用 lerna 来同时管理多个包. ( lerna  的好处特别多,对于依赖关系大,同类型的包推荐使用 lerna 来统一管理。)

核心库是 react-router. react-router-dom 是在浏览器中使用的,react-router-native是在 rn 中使用的。

如果不理解,直接看一下源码就懂了。其实 react-router-dom 只是多了下面四个组件 BrowserRouter、 Link、NavLink、HashRouter, 其他其实都是直接引用 react-router 的。

了解完多包的组织关系之后,我们回到前面如何实现 react-router 的 3个关键步骤,如下:

  1. 如何监听 url 的变化 ?

  2. 如何匹配 path ?

  3. 渲染对应的组件

我们不自己来实现,直接看源码,站在巨人的肩膀上来学习????。接下来我们来看一下 react-router-dom 官方文档 的基本使用。

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

从代码中,我们可以观察到下面几点:

  1. 最外层包裹了<BrowserRouter> ,它有什么意义?

  2. <Route />匹配的外层,包裹了<Switch>,作用是如果匹配了一个,则不会再继续渲染另外一个。如何实现?

  3. Route 中有 path 匹配路径,包裹的则是渲染的组件。

整体设计

我们用一张图来理解一下整个 react-router 是怎么实现的:

接下来我们看看每一个步骤是怎么实现的。

一、监听 URL 的变化

正常情况下,当 URL 发生变化时,浏览器会像服务端发送请求,但使用以下2种办法不会向服务端发送请求:

  • 基于 hash

  • 基于 history

react-router 使用了 history 这个核心库。

1. 选择方式:  history 或 hash

HashRouter 先是从 history 中引用 createBrowserHistory ,然后将 history 和 children 传入到 Router 。BrowseHistory同理。

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

2. 监听 URL 的变化,拿到对应的 history,location,match 等通过 Provider 注入到子组件中。

二、Route 中匹配渲染组件

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

  1. 是否匹配

  2. 渲染组件

1. 是否匹配

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

匹配解析 path ,这里使用了第三方库  path-to-regexp
// Make sure you consistently `decode` segments.
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 方式
<Routepath="/blog/:slug"render={({ match }) => {// Do whatever you want with the match...return <div />;}}
/>// component 方式
<Route path="/user/:username" component={User} />

源码部分如下:

吐槽一下,作者怎么就不能好好用 if else 来写,非要写这么多变态的 ?:,请不要学习,除非你的项目只有你一个前端????。

一下子看不懂也没关系,我们来看下面的流程图。

从上面的代码我们可以看出:

  1. Router 渲染的优先级:children >  component > render,三种方式互斥,只能使用一种。

  2. 不匹配的情况下,只要 children 是函数,也会渲染

  3. component 是使用 createComponent 来创建的, 这会导致不再更新现有组件,而是直接卸载再去挂载一个新的组件。如果是使用匿名函数来传入 component ,每次 render 的时候,这个 props 都不同,会导致重新渲染挂载组件,导致性能特别差。因此,当使用匿名函数的渲染时,请使用 render 或 children 。

// 不要这么使用
<Route path="/user/:username" component={() => <User/> } />

结论

  1. 对于依赖关系大,同类型的包使用 lerna 来统一管理。尽量抽象出共用不可变的地方,比如 react-router 中的方法。

  2. React-router 使用了Compound components(复合组件模式),在这种模式中,组件将被一起使用,它们可以方便的共享一种隐式的状态,比如 Switch , 可以在这里通过 React.children 来控制包裹组件的渲染优先级,而无须使用者去控制。再比如我们经常使用的 <select /><option>, 可以通过 React.children 和 React.cloneElement 来劫持修改子组件,让组件使用者通过更少的 api 来触发更强大的功能。


最近组建了一个江西人的前端交流群,如果你也是江西人可以加我微信 ruochuan12 拉你进群。



················· 若川出品 ·················

今日话题

写作其实是利他共赢的一种方式。长期输出有价值的内容,会给自己带来很多好处。所以交流群里常有人问如何高效学习前端,我有时会建议可以写博客输出。

一个愿景是帮助5年内前端人走向前列的公众号

可加我个人微信 ruochuan12,长期交流学习

推荐阅读

我在阿里招前端,我该怎么帮你?(现在还能加我进模拟面试群)

如何拿下阿里巴巴 P6 的前端 Offer

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/276075.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android 辅助功能_辅助功能简介

android 辅助功能Accessibility may be more than a moral imperative to ensure products are inclusive of more people who already experience barriers in daily life — it has a very practical outcome, benefiting everyone including the person with the disability…

推荐几个前端大佬,真的厉害!

新的一年&#xff0c;推荐几个还在坚持输出的高质量前端公众号&#xff0c;你想要的&#xff1a;React/Vue/Node/工程化/面试经验等&#xff0c; 这里都有。不多说&#xff0c;直接看介绍。???? 程序员成长指北考拉&#xff0c;一个有趣且乐于分享的妹子&#xff0c;专注 N…

RawCode-本身就是实验性的8位类型

What if a type is created solely from the bit-representation of characters?如果仅根据字符的位表示形式创建类型怎么办&#xff1f; This question was the basis of creating this experimental type. The type no longer uses familiar characters. ‘A’ looks like …

2d访问冲突_Light | 基于环形分隔微镜阵列的高速随机访问轴向聚焦系统

撰稿 | OSANJU 刘 扬01导读近日&#xff0c;来自美国加州大学伯克利分校的Rikky Muller教授团队&#xff0c;在国际顶尖学术期刊《Light: Science & Applications》发表了题为“A micromirror array with annular partitioning for high-speed random-access axial focus…

编译出现ARM与THUMB冲突

问题&#xff1a; fatal error LNK1112: module machine type THUMB conflicts with target machine type ARM 解决方法&#xff1a; 在project-setting-linker-command line下&#xff0c;添加/machine:THUMB&#xff0c;如下&#xff1a; 转载于:https://www.cnblogs.com/xfd…

阿里腾讯面试梳理个人成长经历分享

大家好&#xff0c;我是若川。欢迎加我微信 ruochuan12&#xff0c;长期交流学习。今天分享一篇江西师范大学大四同学学习一年前端入职腾讯的经历&#xff0c;关注很久的读者朋友应该知道我也是毕业于江西不那么知名的高校。微信预计阅读只需18分钟。点击下方卡片关注我&#x…

unity 局部照亮_通过著名的艺术家照亮“光与空间运动”

unity 局部照亮Ever since minimalism momentously gained popularity in the 1960’s, the “Light and Space” movement has naturally acquired prominence for its stunning juxtaposition in geometric abstraction, luminescence, and space. Artwork from this distinc…

如何抓住重点,系统高效地学习数据结构与算法?

你是否曾跟我一样&#xff0c;因为看不懂数据结构和算法&#xff0c;而一度怀疑是自己太笨&#xff1f;实际上&#xff0c;很多人在第一次接触这门课时&#xff0c;都会有这种感觉&#xff0c;觉得数据结构和算法很抽象&#xff0c;晦涩难懂&#xff0c;宛如天书。正是这个原因…

Google Map浏览下载器

很久没有更新博客了&#xff0c;最近为了玩Google Map&#xff0c;专门写了个小工具。用以在浏览Google地图时&#xff0c;将关心区域的图片保存到本地。软件主界面如下图&#xff1a; 主界面提取子库主要功能有&#xff1a; 1、可以浏览Google网站的三种图&#xff1a;交通、地…

Node.js 开发者 2020 年度报告

大家好&#xff0c;我是若川。欢迎加我微信 ruochuan12&#xff0c;长期交流学习。今天分享一篇Node.js报告&#xff0c;记得当时我还参与填写这个调查问卷了&#xff0c;Node.js的重要性不言而喻。微信预计阅读只需7分钟。点击下方卡片关注我&#xff0c;或者查看源码系列文章…

视觉设计_视觉设计:

视觉设计What does the customer first see in your application? Yes, its your application design. So it is very important to pay attention to how the design is made. There’s so many factors to include, like how usually people meaning a symbol, how their pr…

开源分布式中间件 DBLE 快速入门指南

2019独角兽企业重金招聘Python工程师标准>>> 环境准备 DBLE项目资料 DBLE官方网站&#xff1a;https://opensource.actionsky.com 可以详细了解DBLE的背景和应用场景&#xff0c;本文不涉及到的细节都可在官方文档获得更细节都信息&#xff1b;对于刚了解到同学&…

文字输入限制_从拼音输入法的兴起看汉字文化圈的衰落

曾经韩国的报纸是这样的&#xff1a;现在的则是这样的&#xff1a;&#xff08;上面两图均来自网络&#xff0c;如有侵权请告知&#xff09;二战之后&#xff0c;曾经广泛使用汉字的朝韩跟越南都走上了废除汉字之路。日本虽然依然使用汉字&#xff0c;却也发布了《当用汉字表》…

跟各位读者朋友分享下公众号运营策略

大家好&#xff0c;我是若川。欢迎加我微信 ruochuan12&#xff0c;加群交流学习。目前我的公众号运营策略&#xff0c;分享给各位读者朋友。点击下方卡片关注我&#xff0c;或者查看源码等系列文章。可能大多数关注的人不知道&#xff0c;我的公众号取名为若川视野的原因。「若…

[轉載]用PHP的ob_start();控制您的浏览器cache!

FROM http://www.phpchina.com/html/28/1628-3870.html Output Control 函数可以让你自由控制脚本中数据的输出。它非常地有用&#xff0c;特别是对于&#xff1a;当你想在数据已经输出后&#xff0c;再输出文件头的情况。输出控制函数不对使用 header() 或 setcookie(), 发送的…

oracle 带有变量的语句_【成都校区】Oracle SQL语句之常见优化方法总结

本帖出自于黑马程序员成都中心&#xff0c;更多资源可关注微信公众号1、SQL语句尽量用大写的&#xff1b;因为oracle总是先解析SQL语句&#xff0c;把小写的字母转换成大写的再执行。2、使用表的别名&#xff1a; 当在SQL语句中连接多个表时, 尽量使用表的别名并把别名前缀于每…

js 获取上下文后面的路径_通过在数据后面显示上下文来可视化公众意见

js 获取上下文后面的路径In 1824, The Harrisburg Pennsylvanian, a newspaper from a town in Pennsylvania conducted the first known public opinion polls in history, and successfully predicted the result of the vote in the close race between Andrew Jackson and …

甘肃甘南步班邮递员:草原上的“递爱”艰途

图为甘肃甘南藏族自治州碌曲县郎木寺镇邮政支局的投递员进行投递工作。&#xff08;资料图&#xff09; 钟欣 摄 中新网兰州1月25日电 (吴玉蒿)渐近春节&#xff0c;刘权英投递邮件的工作愈加繁重。现年45岁的刘权英是甘肃甘南藏族自治州碌曲县郎木寺镇邮政支局的一名投递员。由…

看了就会的 Node.js 三大基础模块常用 API

大家好&#xff0c;我是若川。欢迎加我微信 ruochuan12&#xff0c;加群交流学习。今天分享一篇nodejs基础的文章。点击下方卡片关注我&#xff0c;或者查看源码等系列文章。在日常使用 Node 进行开发的时候&#xff0c;会使用到一些文件系统、路径操作等基础 API&#xff0c;这…

ios设计登录功能_亲爱的产品设计师,这是iOS 14的新功能

ios设计登录功能On June 22, 2020 Apple previewed iOS 14 for the first time. As always there are quite some changes you should know about as a product designer like widgets, pickers, app clips, permissions and more.2020年6月22日&#xff0c;Apple首次预览iOS 1…