偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

React 架構(gòu)的演變 - Hooks 的實(shí)現(xiàn)

開(kāi)發(fā) 架構(gòu)
React Hooks 可以說(shuō)完全顛覆了之前 Class Component 的寫(xiě)法,進(jìn)一步增強(qiáng)了狀態(tài)復(fù)用的能力,讓 Function Component 也具有了內(nèi)部狀態(tài),對(duì)于我個(gè)人來(lái)說(shuō),更加喜歡 Hooks 的寫(xiě)法。

[[348911]]

React Hooks 可以說(shuō)完全顛覆了之前 Class Component 的寫(xiě)法,進(jìn)一步增強(qiáng)了狀態(tài)復(fù)用的能力,讓 Function Component 也具有了內(nèi)部狀態(tài),對(duì)于我個(gè)人來(lái)說(shuō),更加喜歡 Hooks 的寫(xiě)法。當(dāng)然如果你是一個(gè)使用 Class Component 的老手,初期上手時(shí)會(huì)覺(jué)得很苦惱,畢竟之前沉淀的很多 HOC、Render Props 組件基本沒(méi)法用。而且之前的 Function Component 是無(wú)副作用的無(wú)狀態(tài)組件,現(xiàn)在又能通過(guò) Hooks 引入狀態(tài),看起來(lái)真的很讓人疑惑。Function Component 的另一個(gè)優(yōu)勢(shì)就是可以完全告別 this ,在 Class Component 里面 this 真的是一個(gè)讓人討厭的東西?? 。

Hook 如何與組件關(guān)聯(lián)

在之前的文章中多次提到,F(xiàn)iber 架構(gòu)下的 updateQueue、effectList 都是鏈表的數(shù)據(jù)結(jié)構(gòu),然后掛載的 Fiber 節(jié)點(diǎn)上。而一個(gè)函數(shù)組件內(nèi)所有的 Hooks 也是通過(guò)鏈表的形式存儲(chǔ)的,最后掛載到 fiber.memoizedState 上。

  1. function App() { 
  2.   const [num, updateNum] = useState(0) 
  3.  
  4.   return <div 
  5.     onClick={() => updateNum(num => num + 1)} 
  6.   >{ num }</div> 
  7.  
  8. export default App 

我們先簡(jiǎn)單看下,調(diào)用 useState 時(shí),構(gòu)造鏈表的過(guò)程:

  1. var workInProgressHook = null 
  2. var HooksDispatcherOnMount = { 
  3.   useState: function (initialState) { 
  4.     return mountState(initialState) 
  5.   } 
  6.  
  7. function function mountState(initialState) { 
  8.   // 新的 Hook 節(jié)點(diǎn) 
  9.   var hook = mountWorkInProgressHook() 
  10.   // 緩存初始值 
  11.   hook.memoizedState = initialState 
  12.   // 構(gòu)造更新隊(duì)列,類(lèi)似于 fiber.updateQueue 
  13.   var queue = hook.queue = { 
  14.     pending: null
  15.     dispatch: null
  16.     lastRenderedState: initialState 
  17.   } 
  18.   // 用于派發(fā)更新 
  19.   var dispatch = queue.dispatch = dispatchAction.bind( 
  20.     null, workInProgress, queue 
  21.   ) 
  22.   // [num, updateNum] = useState(0) 
  23.   return [hook.memoizedState, dispatch] 
  24.  
  25. function mountWorkInProgressHook() { 
  26.   var hook = { 
  27.     memoizedState: null
  28.     baseState: null
  29.     baseQueue: null
  30.     queue: null
  31.     nextnull 
  32.   } 
  33.  
  34.   if (workInProgressHook === null) { 
  35.     // 構(gòu)造鏈表頭節(jié)點(diǎn) 
  36.     workInProgress.memoizedState = workInProgressHook = hook 
  37.   } else { 
  38.     // 如果鏈表已經(jīng)存在,在掛載到 next 
  39.     workInProgressHook = workInProgressHook.next = hook 
  40.   } 
  41.  
  42.   return workInProgressHook 

如果此時(shí)有兩個(gè) Hook,第二個(gè) Hook 就會(huì)掛載到第一個(gè) Hook 的 next 屬性上。

  1. function App() { 
  2.   const [num, updateNum] = useState(0) 
  3.   const [str, updateStr] = useState('value: '
  4.  
  5.   return <div 
  6.     onClick={() => updateNum(num => num + 1)} 
  7.   >{ str } { num }</div> 
  8.  
  9. export default App 

Hook

Hook 的更新隊(duì)列

Hook 通過(guò) .next 彼此相連,而每個(gè) Hook 對(duì)象下,還有個(gè) queue 字段,該字段和 Fiber 節(jié)點(diǎn)上的 updateQueue 一樣,是一個(gè)更新隊(duì)列在,上篇文章 《React 架構(gòu)的演變-更新機(jī)制》中有講到,React Fiber 架構(gòu)中,更新隊(duì)列通過(guò)鏈表結(jié)構(gòu)進(jìn)行存儲(chǔ)。

  1. class App extends React.Component { 
  2.   state = { val: 0 } 
  3.   click () { 
  4.     for (let i = 0; i < 3; i++) { 
  5.       this.setState({ val: this.state.val + 1 }) 
  6.     } 
  7.   } 
  8.   render() { 
  9.     return <div onClick={() => { 
  10.       this.click() 
  11.     }}>val: { this.state.val }</div> 
  12.   } 

點(diǎn)擊 div 之后,產(chǎn)生的 3 次 setState 通過(guò)鏈表的形式掛載到 fiber.updateQueue 上,待到 MessageChannel 收到通知后,真正執(zhí)行更新操作時(shí),取出更新隊(duì)列,將計(jì)算結(jié)果更新到 fiber.memoizedState。

setState

而 hook.queue 的邏輯和 fiber.updateQueue 的邏輯也是完全一致的。

  1. function App() { 
  2.   const [num, updateNum] = useState(0) 
  3.  
  4.   return <div 
  5.     onClick={() => { 
  6.       // 連續(xù)更新 3 次 
  7.       updateNum(num => num + 1) 
  8.       updateNum(num => num + 1) 
  9.       updateNum(num => num + 1) 
  10.     }} 
  11.   > 
  12.     { num } 
  13.   </div> 
  14.  
  15. export default App; 
  16. var dispatch = queue.dispatch = dispatchAction.bind( 
  17.   null, workInProgress, queue 
  18. // [num, updateNum] = useState(0) 
  19. return [hook.memoizedState, dispatch] 

調(diào)用 useState 的時(shí)候,返回的數(shù)組第二個(gè)參數(shù)為 dispatch,而 dispatch 由 dispatchAction bind 后得到。

  1. function dispatchAction(fiber, queue, action) { 
  2.   var update = { 
  3.     nextnull
  4.     actionaction
  5.     // 省略調(diào)度相關(guān)的參數(shù)... 
  6.   }; 
  7.  
  8.   var pending = queue.pending 
  9.   if (pending === null) { 
  10.     update.next = update 
  11.   } else { 
  12.     update.next = pending.next 
  13.     pending.next = update 
  14.   } 
  15.   queue.pending = update 
  16.  
  17.   // 執(zhí)行更新 
  18.   scheduleUpdateOnFiber() 

可以看到這里構(gòu)造鏈表的方式與 fiber.updateQueue 如出一轍。之前我們通過(guò) updateNum 對(duì) num 連續(xù)更新了 3 次,最后形成的更新隊(duì)列如下:

更新隊(duì)列

函數(shù)組件的更新

前面的文章分享過(guò),F(xiàn)iber 架構(gòu)下的更新流程分為遞(beginWork)、歸(completeWork)兩個(gè)步驟,在 beginWork 中,會(huì)依據(jù)組件類(lèi)型進(jìn)行 render 操作構(gòu)造子組件。

  1. function beginWork(current, workInProgress) { 
  2.   switch (workInProgress.tag) { 
  3.     // 其他類(lèi)型組件代碼省略... 
  4.     case FunctionComponent: { 
  5.       // 這里的 type 就是函數(shù)組件的函數(shù) 
  6.       // 例如,前面的 App 組件,type 就是 function App() {} 
  7.       var Component = workInProgress.type 
  8.       var resolvedProps = workInProgress.pendingProps 
  9.       // 組件更新 
  10.       return updateFunctionComponent( 
  11.         current, workInProgress, Component, resolvedProps 
  12.       ) 
  13.     } 
  14.   } 
  15.  
  16. function updateFunctionComponent( 
  17.  current, workInProgress, Component, nextProps 
  18. ) { 
  19.   // 構(gòu)造子組件 
  20.   var nextChildren = renderWithHooks( 
  21.     current, workInProgress, Component, nextProps 
  22.   ) 
  23.   reconcileChildren(current, workInProgress, nextChildren) 
  24.   return workInProgress.child 

看名字就能看出來(lái),renderWithHooks 方法就是構(gòu)造帶 Hooks 的子組件。

  1. function renderWithHooks( 
  2.  current, workInProgress, Component, props 
  3. ) { 
  4.   if (current !== null && current.memoizedState !== null) { 
  5.     ReactCurrentDispatcher.current = HooksDispatcherOnUpdate 
  6.   } else { 
  7.     ReactCurrentDispatcher.current = HooksDispatcherOnMount 
  8.   } 
  9.   var children = Component(props) 
  10.   return children 

從上面的代碼可以看出,函數(shù)組件更新或者首次渲染時(shí),本質(zhì)就是將函數(shù)取出執(zhí)行了一遍。不同的地方在于給 ReactCurrentDispatcher 進(jìn)行了不同的賦值,而 ReactCurrentDispatcher 的值最終會(huì)影響 useState 調(diào)用不同的方法。

根據(jù)之前文章講過(guò)的雙緩存機(jī)制,current 存在的時(shí)候表示是更新操作,不存在的時(shí)候表示首次渲染。

  1. function useState(initialState) { 
  2.   // 首次渲染時(shí)指向 HooksDispatcherOnMount 
  3.   // 更新操作時(shí)指向 HooksDispatcherOnUpdate 
  4.   var dispatcher = ReactCurrentDispatcher.current 
  5.   return dispatcher.useState(initialState) 

HooksDispatcherOnMount.useState 的代碼前面已經(jīng)介紹過(guò),這里不再著重介紹。

  1. // HooksDispatcherOnMount 的代碼前面已經(jīng)介紹過(guò) 
  2. var HooksDispatcherOnMount = { 
  3.   useState: function (initialState) { 
  4.     return mountState(initialState) 
  5.   } 

我們重點(diǎn)看看 HooksDispatcherOnMount.useState 的邏輯。

  1. var HooksDispatcherOnUpdateInDEV = { 
  2.   useState: function (initialState) { 
  3.     return updateState() 
  4.   } 
  5.  
  6. function updateState() { 
  7.   // 取出當(dāng)前 hook 
  8.   workInProgressHook = nextWorkInProgressHook 
  9.   nextWorkInProgressHook = workInProgressHook.next 
  10.  
  11.   var hook = nextWorkInProgressHook 
  12.   var queue = hook.queue 
  13.   var pendingQueue = queue.pending 
  14.  
  15.   // 處理更新 
  16.   var first = pendingQueue.next 
  17.   var state = hook.memoizedState 
  18.   var update = first 
  19.  
  20.   do { 
  21.     var action = update.action 
  22.     state = typeof action === 'function' ? action(state) : action 
  23.  
  24.     update = update.next
  25.   } while (update !== null && update !== first
  26.  
  27.  
  28.   hook.memoizedState = state 
  29.  
  30.   var dispatch = queue.dispatch 
  31.   return [hook.memoizedState, dispatch] 

如果有看之前的 setState 的代碼,這里的邏輯其實(shí)是一樣的。將更新對(duì)象的 action 取出,如果是函數(shù)就執(zhí)行,如果不是函數(shù)就直接對(duì) state 進(jìn)行替換操作。

總結(jié)

React 系列的文章終于寫(xiě)完了,這一篇文章應(yīng)該是最簡(jiǎn)單的一篇,如果想拋開(kāi) React 源碼,單獨(dú)看 Hooks 實(shí)現(xiàn)可以看這篇文章:《React Hooks 原理》。Fiber 架構(gòu)為了能夠?qū)崿F(xiàn)循環(huán)的方式更新,將所有涉及到數(shù)據(jù)的地方結(jié)構(gòu)都改成了鏈表,這樣的優(yōu)勢(shì)就是可以隨時(shí)中斷,為異步模式讓路,F(xiàn)iber 樹(shù)就像一顆圣誕樹(shù),上面掛滿了各種彩燈(alternate、EffectList、updateQueue、Hooks)。

本文轉(zhuǎn)載自微信公眾號(hào)「 更了不起的前端  」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系更了不起的前端  公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: 更了不起的前端
相關(guān)推薦

2022-04-16 20:10:00

React Hookfiber框架

2020-09-30 09:15:24

React架構(gòu)遞歸

2020-10-13 08:36:30

React 架構(gòu)機(jī)制

2020-09-24 08:45:10

React架構(gòu)源碼

2022-08-21 09:41:42

ReactVue3前端

2023-11-06 08:00:00

ReactJavaScript開(kāi)發(fā)

2022-03-31 17:54:29

ReactHooks前端

2020-09-19 17:46:20

React Hooks開(kāi)發(fā)函數(shù)

2024-06-04 14:17:26

2019-08-20 15:16:26

Reacthooks前端

2023-05-29 13:56:00

JSReact

2021-03-18 08:00:55

組件Hooks React

2021-05-11 08:48:23

React Hooks前端

2023-05-08 07:52:29

JSXReactHooks

2022-02-10 19:15:18

React監(jiān)聽(tīng)系統(tǒng)模式

2020-08-10 06:31:01

React Hooks前端開(kāi)發(fā)

2021-11-05 10:36:19

性能優(yōu)化實(shí)踐

2022-03-16 22:24:50

ReactstateHooks

2019-03-13 10:10:26

React組件前端

2022-11-15 17:31:35

邊緣計(jì)算架構(gòu)人工智能
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)