成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

看完 React Conext 源碼,就知道怎么繞過 Provider 修改它了

開發 前端
本文就從源碼層面來講下 cotnext 的原理,而且我們能從中發現一些 hack 的小技巧。

context 是 React 提供的特性,可以實現任意層級組件之間的數據傳遞。

可能大家用過 context,但是不知道它是怎么實現的。

本文就從源碼層面來講下 cotnext 的原理,而且我們能從中發現一些 hack 的小技巧。

首先,我們先過一下 context 的使用方式:

context 的使用

有這樣的 3個組件,One、Two、Three:

我們想不通過 props 從 One 傳遞數據到 Three,這時候就可以用 context 了:

調用 createContext 來創建 context 對象,初始化數據是 'dong'。

Three 組件里就可以通過 useContext 把 context 數據取出來了:

除了初始化的時候可以傳入,后面也可以通過 Provider 來修改 context 數據:

我們通過 Provider 傳入了新的 value,覆蓋了初始值,這樣 useContext 拿到的值就變了:

函數組件是用 useContext 的 hook 來取,class 組件則是用 Consumer 來?。?/p>

我們依然是通過 createContext 創建 context 對象,通過 Provider 修改了 value。

現在 Three 變成了 class 組件,所以使用 context 的方式要通過 Consumer,它的 children 部分傳入 render 函數,參數里就能拿到 context 數據:

這分別是 class 組件和 function 組件使用 context 的方式,我們小結一下:

context 使用 React.createContext 創建,可以傳入初始值,后面也可以通過 Provider 來修改其中的值,使用 context 值的時候,如果是 function 組件,可以通過 useContext 的 hook 來取,而 class 組件是用 Consumer 傳入一個 render 函數的方式來取。

學會了 context 怎么用,我們再來看下它的實現原理:

context 的實現

首先我們看下 createContext 的源碼:

它創建了一個 context 對象,有 _currentValue 屬性,一看就是保存值的,還有 Consumer 和 Provider 兩個屬性。

Consumer 和 Provider 都是通過 _context 保存了 context 對象的引用。

并且它們都有 $$typeof 來標識類型。

這就是 context 對象的結構:

那這個 context 對象是怎么結合到 React 渲染流程里的呢?

就是通過 jsx 結合的呀:

jsx 編譯以后會產生 render function。

比如上面那段 jsx 編譯后是這樣的:

新版 React 不調用 React.createElement 了,而是 jsx 函數,也就是上面的 jsxDev。

看這第一個參數是啥,有 $$typeof 和 _context 屬性,不就是我們傳入的 context.Provider 么:

value 是在 props 參數里傳入的:

jsx 執行會產生一個個 vdom:

這樣 context 就保存到了 vdom 節點上。

那遞歸渲染的時候不就能從 vdom 拿到 context 了么?

別著急,React 現在不是 vdom 直接渲染了,而是會先把 vdom 轉成 fiber,這個過程叫做 reconcile:

fiber 的結構中保存了兄弟節點和父節點的引用,這是 vdom 所沒有的,vdom 只有子節點的引用,所以 vdom 變成 fiber 以后就變成了可打斷的,因為就算斷了也能找到兄弟節點和父節點繼續處理。

那保存在 vdom 中的 context 不就自然的轉移到了 fiber 節點上了么?

創建 fiber 的代碼是這樣的:

調用 createFiber 最終會 new FiberNode

然后也是創建一個對象:

這個和 vdom 差別不大,只不過屬性不一樣了。

你看這里的 fiber.type,不就是保存在 vdom 上的 context.Provider 對象么?

那之后這個 fiber 節點是怎么處理的呢?

你會發現在處理 fiber 節點的時候,會判斷 fiber.tag,不同的類型做不同的處理:

FunctionComponent 和 ClassComponent 的 fiber 節點的處理就不同。

下面可以找到 ContextProvider 的:

它的實現就是修改了 context 中的值:

pushProvider 就是最終修改 context 值的地方:

通過 _context 拿到了 Provider 所引用的 context 對象,然后修改它的 _currentValue 屬性,也就是 context 中的值。

對照著這個 context 對象的結構一看就明白了:

同理,后面處理到 Consumer 的時候也是這樣拿到 context 的:

ContextConsumer 的 fiber 節點也會做專門的處理:

workInProgress 就是當前 fiber 節點,它的 type 保存了 context.Consumer 對象。

我們通過 readContext 拿到其中的值,也就是取 _currentValue 屬性。

拿到最新的 context 中的值后就觸發子組件的渲染:

所以說為什么 Consumer 必須要傳入一個 render 函數作為子節點不就清楚了么:

這樣我們就取到了 context 中的值,并觸發了子組件的渲染。

使用 context 的方式還有 useContext 的 hook,其實那個也是一樣的:

useContext 也是調用了 readContext 來讀取了 context 的 _currentValue 屬性:

當然,useContext 的 context 不是從 fiber.type 來取的,而是用的傳入的 context:

但是都是引用的同一個對象,在 Provider 里修改了 context 的 value,這里取到的 context 同樣也是新的。

我們小結下 context 的實現原理:

createContext 會創建 context 對象,它有 _currentValue 保存值,還引用了 Provider 和 Consumer 兩個對象,Provider 和 Consumer 里有 _context 屬性引用了 context。

在 jsx 渲染的時候會把 Provider 和 Consumer 對象保存到 vdom 上,后面 reconcile 的時候會轉移到 fiber 的 type 屬性上,處理 fiber 節點的時候會根據類型做不同的處理:

如果是 Provider,就會根據傳入的 value 修改 context 的值,也就是 _currentValue 屬性

如果是 Consumer,則會讀取 context 的值然后觸發子組件的渲染。

函數組件會使用 useContext 的 hook,最終也是讀取了同一個 context 的 _currentValue 的 值

理清了 context 的實現原理,我們是不是能發現一些 hack 的技巧呢?

比如 Provider 其實就是修改 _currentValue 的,那我們自己修改 context._currentValue 不就不用 Provider 了?

試一下:

用 Provider 是這樣的:

其實直接修改 _currentValue 也可以:

但是不推薦這樣寫,因為這是個私有屬性,萬一哪一天變了呢?

總結

context 是 React 提供的任意層級組件之間通信的機制,我們先過了一遍它的使用方式:

通過 createContext 來創建 context 對象,可以傳入初始值,可以通過 jsx 里的 context.Provider 來修改 context 值,通過 context.Consumer 來拿到 context 的值,如果是函數組件,是通過 useContext 的 hook 來取。

然后通過源碼理清了 context 的實現原理:

jsx 里的 Provider 和 Consumer 對象會被保存到 vdom 中,最后會轉移到 fiber 節點的 type 屬性上,fiber 處理的時候,對 Provider 會修改 context 的 value,而 Consumer 則會取出 context 的值然后觸發子組件渲染。函數組件的 useContext 的 hook 也是從同一個 context 對象讀取的數據。

然后我們發現了繞過 Provider 修改 context 的方式,就是直接修改 _currentValue,但是不推薦這樣做,因為私有屬性不一定啥時候就變了。

context 是這樣實現的,其他特性的實現原理也大同小異,只不過是掛到了不同的屬性上,處理 fiber 的時候做了分成了不同的類型來處理。

理清 context 的原理之后,你是否對 vdom、fiber,還有 reconcile 的過程都有更深的理解了呢?

責任編輯:姜華 來源: 神光的編程秘籍
相關推薦

2019-06-05 15:20:00

MongoDBNoSQL數據庫

2020-07-20 10:20:30

this前端代碼

2022-07-01 13:38:48

霧計算邊緣計算

2022-08-25 09:08:40

微服務架構

2016-03-09 19:52:02

無線應用Wi-Fi定位

2021-03-14 15:58:26

手機定位系統

2019-05-16 08:51:22

物聯網獲利IOT

2023-10-08 08:41:04

JavaPython編程語言

2022-09-02 19:10:46

高并發架構系統

2021-05-07 06:15:32

編程開發端口掃描

2018-10-31 11:41:49

Python代碼語言

2020-02-18 16:53:48

機械硬盤SMRPMR

2019-12-12 10:02:29

滴滴數據中臺

2022-01-17 21:13:32

Windows 10Windows微軟

2023-05-09 13:55:08

GPT-4AI

2020-05-22 13:00:45

蘋果安卓手機

2019-08-02 15:35:02

工具代碼開發

2015-08-14 10:07:06

2018-07-24 11:21:25

程序員加班月薪

2020-06-28 07:49:06

WiFi 6WiFi 5網絡技術
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 一区二区国产在线 | 欧美精品一区三区 | 久久最新精品 | 日韩三片 | 欧美日韩一区二区在线观看 | 国产精品久久久久一区二区三区 | 亚洲精品视频在线播放 | 亚洲精品国产电影 | 国产中的精品av涩差av | 精品乱码一区二区 | 久久亚洲一区二区三区四区 | 久久婷婷av | 亚洲成人一级 | 国产精品乱码一二三区的特点 | 免费h视频 | 日韩不卡一区二区三区 | 久久狠狠 | 综合二区 | 狠狠躁18三区二区一区 | 精品视频一区二区三区 | 亚洲国产aⅴ成人精品无吗 综合国产在线 | 婷婷二区 | 欧美亚洲另类在线 | 精品区一区二区 | 乱码av午夜噜噜噜噜动漫 | 久久久久久免费毛片精品 | 狠狠色狠狠色综合日日92 | 欧美成人在线免费 | 欧美精品久久久久久久久久 | 99这里只有精品 | 国产乱码精品1区2区3区 | 黄网站涩免费蜜桃网站 | 免费一级淫片aaa片毛片a级 | 精品国产一区二区三区久久 | 国产一区二区三区在线 | 久久精品色视频 | 国产精品久久久久久久免费大片 | 欧美精品一区三区 | 日韩中文字幕网 | 国产精品欧美一区二区三区不卡 | 久久国产精品网 |