圖解 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.pswp.cn/news/276075.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/276075.shtml
英文地址,請注明出處:http://en.pswp.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…