今天咱們React
源碼交流羣裏有個小夥伴提出個有趣的問題,以爲本身對React
運行流程理解很到位的同窗,能夠來看看。前端
對於以下Demo,點擊Parent
組件的div
,觸發更新,Son
組件會打印child render!
麼?數組
function Son() {
console.log('child render!'); return <div>Son</div>; } function Parent(props) { const [count, setCount] = React.useState(0); return ( <div onClick={() => {setCount(count + 1)}}> count:{count} {props.children} </div> ); } function App() { return ( <Parent> <Son/> </Parent> ); } const rootEl = document.querySelector("#root"); ReactDOM.render(<App/>, rootEl); 複製代碼
在線Demo地址markdown
👉右滑顯示答案: 不會
複製代碼
React
建立Fiber樹
時,每一個組件對應的fiber
都是經過以下兩個邏輯之一建立的:函數
render。即調用render
函數,根據返回的JSX
建立新的fiber
。oop
bailout。即知足必定條件時,React
判斷該組件在更新先後沒有發生變化,則複用該組件在上一次更新的fiber
做爲本次更新的fiber
。ui
能夠看到,當命中bailout
邏輯時,是不會調用render
函數的。spa
因此,Son
組件不會打印child render!
是由於命中了bailout
邏輯。code
什麼狀況下會進入bailout
邏輯?當同時知足以下4個條件時:orm
即本次更新的props
(newProps)不等於上次更新的props
(oldProps)。xml
注意這裏是全等比較。
咱們知道組件render
會返回JSX
,JSX
是React.createElement
的語法糖。
因此render
的返回結果其實是React.createElement
的執行結果,即一個包含props
屬性的對象。
即便本次更新與上次更新props
中每一項參數都沒有變化,可是本次更新是React.createElement
的執行結果,是一個全新的props
引用,因此oldProps !== newProps
。
若是咱們使用了PureComponent
或Memo
,那麼在判斷是進入render
仍是bailout
時,不會判斷oldProps
與newProps
是否全等,而是會對props
內每一個屬性進行淺比較。
即context
的value
沒有變化。
更新先後fiber.type
是否變化,好比div
是否變爲p
。
當前fiber
上是否存在更新
,若是存在那麼更新
的優先級
是否和本次整棵fiber樹
調度的優先級
一致?
若是一致則進入render
邏輯。
就咱們的Demo來講,Parent
是整棵樹中惟一能觸發更新
的組件(經過調用setCount
)。
因此Parent
對應的fiber
是惟一知足條件4的fiber
。
因此,Demo中Son
進入bailout
邏輯,必定是同時知足以上4個條件。咱們一個個來看。
條件2,Demo中沒有用到context
,知足。
條件3,更新先後type
都爲Son
對應的函數組件,知足。
條件4,Son
自己沒法觸發更新,知足。
因此,重點是條件1。讓咱們詳細來看下。
本次更新開始時,Fiber樹
存在以下2個fiber
:
FiberRootNode | RootFiber 複製代碼
其中FiberRootNode
是整個應用的根節點,RootFiber
是調用ReactDOM.render
建立的fiber
。
首先,RootFiber
會進入bailout
的邏輯,因此返回的App fiber
和更新前是一致的。
FiberRootNode | RootFiber | App fiber 複製代碼
因爲App fiber
是RootFiber
走bailout
邏輯返回的,因此對於App fiber
,oldProps === newProps
。而且bailout
剩下3個條件也知足。
因此App fiber
也會走bailout
邏輯,返回Parent fiber
。
FiberRootNode | RootFiber | App fiber | Parent fiber 複製代碼
因爲更新是Parent fiber
觸發的,因此他不知足條件4,會走render
的邏輯。
接下來是關鍵
若是render
返回的Son
是以下形式:
<Son/>
複製代碼
會編譯爲
React.createElement(Son, null)
複製代碼
執行後返回JSX
。
因爲props
的引用改變,oldProps !== newProps
。會走render
邏輯。
可是在Demo中Son
是以下形式:
{props.children}
複製代碼
其中,props.children
是Son
對應的JSX
,而這裏的props
是App fiber
走bailout
邏輯後返回的。
因此Son
對應的JSX
與上次更新時一致,JSX
中保存的props
也就一致,知足條件1。
能夠看到,Son
知足bailout
的全部條件,因此不會render
。
當你理解這4個條件後,對於React
組件更新會有全新的認識。
不得不說,React
真是太難了,打工人流下了不爭氣的眼淚。
關注公衆號,進源碼羣,和其餘前端打工人一塊兒成長。