react16中的hooks的底層實(shí)現(xiàn)原理
好的 ??,你問(wèn)的是 React 16 中所有 Hooks 的底層實(shí)現(xiàn)和原理。
這里我會(huì)分幾個(gè)層次來(lái)解釋:
- React 16 的背景
- Hooks 的設(shè)計(jì)理念
- 核心 Hooks 的底層實(shí)現(xiàn)思路(
useState,useEffect,useContext, …) - React 內(nèi)部的 Hook 數(shù)據(jù)結(jié)構(gòu)
- 執(zhí)行流程(函數(shù)組件渲染時(shí) Hook 的調(diào)用順序)
1. 背景:React 16 與 Hooks
- React 16.8 是第一個(gè)支持 Hooks 的版本。
- 目標(biāo):讓函數(shù)組件具備狀態(tài)(state)和副作用(effect)的能力,替代部分 class 組件邏輯(比如
componentDidMount/componentDidUpdate/componentWillUnmount)。 - 核心思想:通過(guò)“調(diào)用順序”來(lái)匹配 Hook 與組件的 fiber 節(jié)點(diǎn),避免顯式 id。
2. Hooks 的設(shè)計(jì)理念
React Hooks 有幾個(gè)原則:
- 只能在函數(shù)組件頂層調(diào)用,不能在條件語(yǔ)句或循環(huán)里動(dòng)態(tài)調(diào)用。
- 這是因?yàn)?Hooks 依賴調(diào)用順序來(lái)定位。
- 只能在 React 函數(shù)組件或自定義 Hook 中調(diào)用。
- 每個(gè)組件 fiber 節(jié)點(diǎn)維護(hù)一個(gè)單鏈表,鏈表節(jié)點(diǎn)就是 Hook 對(duì)象。
?? 這就是為什么 eslint-plugin-react-hooks 會(huì)強(qiáng)制規(guī)則:不要在 if/for 中調(diào)用 Hook。
3. 常見 Hooks 的底層原理
(1)useState
- 本質(zhì)是
useReducer的語(yǔ)法糖。 - 內(nèi)部維護(hù)一個(gè) Hook 節(jié)點(diǎn)對(duì)象(鏈表的一部分):
type Hook = {
memoizedState: any, // 當(dāng)前的 state
baseState: any, // 初始 state
queue: UpdateQueue, // 更新隊(duì)列(鏈表結(jié)構(gòu))
next: Hook | null // 指向下一個(gè) Hook
}
- 當(dāng)你調(diào)用
useState(initial)時(shí): - 創(chuàng)建一個(gè)更新對(duì)象(
action)。 - 掛到
queue上。 - 觸發(fā)
scheduleUpdateOnFiber,進(jìn)入調(diào)度。
- 創(chuàng)建一個(gè)更新對(duì)象(
- React 在當(dāng)前 fiber 上創(chuàng)建一個(gè) Hook 節(jié)點(diǎn),
memoizedState = initial。 - 返回
[state, dispatch]。 dispatch本質(zhì)是一個(gè)函數(shù):
- React 在當(dāng)前 fiber 上創(chuàng)建一個(gè) Hook 節(jié)點(diǎn),
- 渲染時(shí),React 會(huì)執(zhí)行
processUpdateQueue,消費(fèi)queue,更新memoizedState。
(2)useEffect
- 保存副作用函數(shù)和依賴數(shù)組。
- Hook 節(jié)點(diǎn)結(jié)構(gòu):
type Effect = {
tag: HookFlags, // effect 類型(Passive, Layout 等)
create: () => (() => void) | void, // 副作用函數(shù)
destroy?: () => void, // 清理函數(shù)
deps: any[] | null, // 依賴
next: Effect | null
}
- 調(diào)用
useEffect(fn, deps)時(shí): - 如果
deps變了,就調(diào)用上一次的destroy(清理),再執(zhí)行新的create。 - 如果沒變,則跳過(guò)。
- 如果
- React 在 Hook 節(jié)點(diǎn)里記錄
create和deps。 - 在 commit 階段(DOM 更新后),React 比較新舊依賴:
- React 在 Hook 節(jié)點(diǎn)里記錄
(3)useLayoutEffect
- 和
useEffect類似,但執(zhí)行時(shí)機(jī)不同: useLayoutEffect在 DOM 更新后、瀏覽器繪制前 執(zhí)行(同步)。useEffect在 瀏覽器繪制后 執(zhí)行(異步,延遲)。
- 用于測(cè)量 DOM、同步操作。
(4)useContext
- 獲取 Context 的值。
- 機(jī)制:
- 組件調(diào)用
useContext(MyContext)時(shí),會(huì)在 fiber 上記錄對(duì)MyContext的依賴。 - 當(dāng) Context Provider 的值變化時(shí),React 會(huì)找到所有依賴它的消費(fèi) fiber,觸發(fā)更新。
- 組件調(diào)用
(5)useReducer
- 類似 Redux 的 reducer:
useState底層其實(shí)就是useReducer的特例:useState(initial) === useReducer((s, a) => typeof a === 'function' ? a(s) : a, initial)
- 保存 reducer 和
state,通過(guò)dispatch觸發(fā)更新。
(6)useRef
- 維護(hù)一個(gè)
{ current: any }對(duì)象,整個(gè)生命周期內(nèi)保持引用不變。 - 實(shí)現(xiàn):只在 Hook 節(jié)點(diǎn)里存一個(gè)
memoizedState = { current: initialValue },之后不會(huì)更新。
(7)useMemo/useCallback
- 本質(zhì)是“帶緩存的值 / 函數(shù)”。
- Hook 節(jié)點(diǎn)保存
value和deps。 - 渲染時(shí)對(duì)比依賴數(shù)組:
- 如果沒變,直接返回舊值。
- 如果變了,重新計(jì)算并緩存。
4. React 內(nèi)部 Hook 數(shù)據(jù)結(jié)構(gòu)
在 fiber 節(jié)點(diǎn)上,每個(gè)函數(shù)組件對(duì)應(yīng)一個(gè) Hook 鏈表:
type Fiber = {
memoizedState: Hook | null; // hook 鏈表頭
updateQueue: any;
// ...
}
renderWithHooks函數(shù):- 進(jìn)入函數(shù)組件渲染時(shí),初始化
currentlyRenderingFiber。 - 每調(diào)用一個(gè) Hook,就在鏈表上走一步,復(fù)用或新建節(jié)點(diǎn)。
- 保證 “調(diào)用順序一致 = Hook 順序一致”。
- 進(jìn)入函數(shù)組件渲染時(shí),初始化
5. Hook 執(zhí)行流程總結(jié)
- 初次渲染
- 執(zhí)行函數(shù)組件,依次調(diào)用
useState/useEffect… - 為每個(gè) Hook 創(chuàng)建一個(gè) Hook 節(jié)點(diǎn),掛到 fiber.memoizedState 鏈表上。
- 執(zhí)行函數(shù)組件,依次調(diào)用
- 更新渲染
- 執(zhí)行組件時(shí),React 會(huì)遍歷 fiber.memoizedState 鏈表。
- 每次調(diào)用 Hook,就拿到對(duì)應(yīng)節(jié)點(diǎn),復(fù)用之前的狀態(tài)。
- 如果調(diào)用順序錯(cuò)了(比如把 Hook 放到 if 里),鏈表錯(cuò)位,React 會(huì)報(bào)錯(cuò)。
- 提交階段(commit phase)
- 對(duì)于
useEffect/useLayoutEffect,會(huì)根據(jù)依賴比較來(lái)執(zhí)行副作用。 - 對(duì)于
useState/useReducer,會(huì)處理更新隊(duì)列,得到新 state。
- 對(duì)于
? 總結(jié):
- React Hooks 的底層實(shí)現(xiàn)是 fiber 節(jié)點(diǎn)上的 Hook 鏈表 + 調(diào)用順序保證。
- 每個(gè) Hook 維護(hù)一個(gè)小的狀態(tài)對(duì)象,存儲(chǔ)
memoizedState、queue、deps等。 useState基于更新隊(duì)列;useEffect延遲到 commit 階段執(zhí)行;useRef只是一個(gè)穩(wěn)定對(duì)象;useMemo/useCallback做依賴比較。
到此這篇關(guān)于react16中的hooks的底層實(shí)現(xiàn)原理的文章就介紹到這了,更多相關(guān)react16 hooks底層原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- React Hooks 鉤子的具體使用
- 使用React Hooks模擬類組件的生命周期方法
- react中函數(shù)式組件React Hooks詳解
- React使用Hooks從服務(wù)端獲取數(shù)據(jù)的完整指南
- React中不適當(dāng)?shù)腍ooks使用問(wèn)題及解決方案
- React?中hooks之?React.memo?和?useMemo用法示例總結(jié)
- react hooks實(shí)現(xiàn)防抖節(jié)流的方法小結(jié)
- React?Hooks的useState、useRef使用小結(jié)
- react中hooks使用useState的更新不觸發(fā)dom更新問(wèn)題及解決
相關(guān)文章
React不能將useMemo設(shè)置為默認(rèn)方法原因詳解
這篇文章主要為大家介紹了React不能將useMemo設(shè)置為默認(rèn)方法原因詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>2022-07-07
react-redux action傳參及多個(gè)state處理的實(shí)現(xiàn)
本文主要介紹了react-redux action傳參及多個(gè)state處理的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
關(guān)于React項(xiàng)目中的PDF展示解決方案
這篇文章主要介紹了關(guān)于React項(xiàng)目中的PDF展示解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
React Hooks獲取數(shù)據(jù)實(shí)現(xiàn)方法介紹
這篇文章主要介紹了react hooks獲取數(shù)據(jù),文中給大家介紹了useState dispatch函數(shù)如何與其使用的Function Component進(jìn)行綁定,實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-10-10
React Native自定義標(biāo)題欄組件的實(shí)現(xiàn)方法
今天講一下如何實(shí)現(xiàn)自定義標(biāo)題欄組件,我們都知道RN有一個(gè)優(yōu)點(diǎn)就是可以組件化,在需要使用該組件的地方直接引用并傳遞一些參數(shù)就可以了,這種方式確實(shí)提高了開發(fā)效率。對(duì)React Native自定義標(biāo)題欄組件的實(shí)現(xiàn)方法感興趣的朋友參考下2017-01-01

