图解 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,一经查实,立即删除!

相关文章

青海西宁市大通县非洲猪瘟疫区解除封锁

中新网1月23日电 据农业农村部网站消息&#xff0c;农业农村部接到青海省农业农村厅报告&#xff0c;经评估验收合格&#xff0c;青海省西宁市大通县非洲猪瘟疫区解除封锁。 大通县非洲猪瘟疫情发生后&#xff0c;当地按照非洲猪瘟疫情应急预案和非洲猪瘟防治技术规范要求&…

回顾:中网饱经沧桑劫后余生 万平国回首艰辛历程

转载链接&#xff1a;http://tech.sina.com.cn/i/c/75586.shtml 回顾&#xff1a;中网饱经沧桑劫后余生 万平国回首艰辛历程 http://www.sina.com.cn 2001年07月12日 13:25 财经时报   “最早的聊天室是我们做的、最早的检索也是中网公司的、中网公司也是最早做电子商务的数…

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…

【columnstore】mariadb columnstore 数据迁移

通过cpimport传输迁移&#xff1a; 官方文档&#xff1a;https://mariadb.com/kb/en/lib...步骤&#xff1a; 1.导出库中表的表结构&#xff0c;不要数据 # mysqldump -h 127.0.0.1 -uroot -p -P 4066 --skip-lock-tables test -d > test.sql 2.nc将文件传输至目标机器192.…

我有一个梦想

我有一个梦想&#xff0c;做一名大师傅&#xff0c;快乐地下厨房。土豆在我刀下歌唱&#xff0c;茄子在我锅里跳舞。盘碗勺筷为我呐喊&#xff0c;油盐酱醋为我喝彩。 我有一个梦想&#xff0c;让土豆和茄子不再孤单&#xff0c;再加几个青椒&#xff0c;做一盘地三鲜。还要少放…

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

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

MySQL查询结果导出到文件

转载链接&#xff1a;http://eric-gcm.iteye.com/blog/1127734 选择某些行作为需要的数据 SELECT id,dbname FROM index into outfile "d://aaa.txt"; 一般大家都会用 “SELECT INTO OUTFIL”将查询结果导出到文件&#xff0c;但是这种方法不能覆盖或者添加到已经创建…

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…

解决Ubuntu系统中文乱码显示问题

转载链接&#xff1a;http://www.linuxidc.com/Linux/2014-02/96939.htm 一. Ubuntu默认的中文字符编码 Ubuntu默认的中文字符编码为zh_CN.UTF-8&#xff0c;这个可以在 /etc/environment中看到&#xff1a; sudo gedit /etc/environment 可以看到如下内容&#xff1a; PATH&qu…

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;交通、地…

vue xunidom_vue的虚拟dom(Virtual DOM )

模板转换成视图的过程在底层实现中Vue会将模板编译成渲染函数&#xff0c;当然我们也可以不写模板&#xff0c;直接写渲染函数&#xff0c;以获得更好的控制。渲染函数&#xff1a;渲染函数是用来生成Virtual DOM的&#xff1b;VNode虚拟节点&#xff1a;vnode可以理解成dom节点…

mysql之union合并查询

转载链接&#xff1a;http://www.cnblogs.com/zzwlovegfj/archive/2012/06/23/2559592.html union:联合的意思&#xff0c;即把两次或多次查询结果合并起来。 要求&#xff1a;两次查询的列数必须一致 推荐&#xff1a;列的类型可以不一样&#xff0c;但推荐查询的每一列&#…

Node.js 开发者 2020 年度报告

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

[SPS2010] 使用心得 7 - ebook for Installation

[SPS2010] 使用心得 7 - ebook for Installation 一本有关Sharepoint 2010安装的ebook (英语&#xff09; http://sharepoint2007tips.com/Documents/Installing%20and%20Configuring%20SharePoint%202010.pdf 相当详细&#xff0c;目前为止仅有的可以下载的。 posted on 2010-…

视觉设计_视觉设计:

视觉设计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…