天天看點

2023前端必看的React面試知識

作者:HerryLo

React架構

1. JSX本質是什麼,它和JS的關系是什麼?為什麼使用JSX?

JSX是類HTML的文法結構,實質是JS的文法擴張。它文法結構簡潔,通俗易懂,對于研發效率和體驗有大的提升。

2. JSX背後的功能子產品是什麼,這個功能子產品做了哪些事情?

JSX通過babel文法轉換之後,實際就是通過React.createElement函數來建立React元素。createElement接收三個參數type類型,config配置,children子元素。通過createElement就可建立出虛拟DOM對象。

3. react16.3新舊生命周期,消失的舊生命周期有哪些?

去掉ComponentWillMount和CompoentWillReceiveProps 更新為getDeicvedStateFromProps,保證生命周期更加單一,更可控;去掉ComponentWillUpdate新增getSnapshotDeforeUpdate;

4. React團隊為什麼要去掉舊的生命周期函數?

React16+引入Fiber架構,Fiber會将一個大的更新任務拆解為多個小任務,而且它是可中止,可恢複的。React16+生命周期被劃分為render和commit兩個階段。render階段在執行過程中允許被打斷,而commit階段操作涉及真實DOM渲染,是不可打斷的。

`ComponentWillMount`、
`ComponentWillUpdate`、
`ComponentWillReceiveProps`
複制代碼           

這些生命周期,它們都處于render階段,而在Fiber架構中,render階段會被打斷,重複被執行。在這些生命周期可能習慣做的事情可能有:setState、異步請求、操作真實DOM等。而在Fiber異步渲染控制下,這些生命周期可能會導緻非常嚴重的bug(例如在這些廢棄的生命周期中調用支付接口)。

5. React元件資料如何流動?實作資料通信的方案有哪些?

react是自上而下的單向元件資料流

1.父-子元件通過prop屬性傳遞資料,子-父元件可通過函數;
2.兄弟元件共享同一個父元件,達到兄弟元件通信;
3.Context API實作全局通信;(目前還沒試過)
4.redux資料狀态容,進行可預測的狀态管理;
5.釋出/訂閱模式實作任意元件通信;
複制代碼           

6. 為什麼是React Hooks?

相對于Class元件,函數元件更加輕量,更加符合UI=render(data)特點。同時在Fiber架構的加持下,Hooks的實作不是問題。配合函數元件的發展,Hooks應運而生,進而是函數元件真正把資料和渲染綁定到一起。當然Hooks也還是存在部分不足:部分周期不存在;不能很好的消化“複雜”,元件的拆分群組織是一個大的挑戰,不易把握。

7. 為什麼Hooks執行順序如此重要?

Hooks本質是連結清單。例如 使用useText、useState建立state時,hook建立的state會以單連結清單形式儲存,更新時,函數元件重新調用,hooks會依次周遊單連結清單,讀取資料并更新,這一過程完全按照建立時的順序來的。是以當更新時,位置一旦改變,執行順序被替換,運作就會出現bug。

8. 調和(協調)和diff的關系或差別?

調和指的是虛拟DOM映射到真實DOM的過程。調和過程并不能和diff畫等号。調和是“使一緻”的過程,而diff是“找不同”的過程,它隻是“使一緻”過程中的一個環節。(當然常說的調和相關問題多半就是diff過程的)

9. react的diff邏輯和思路?

1.因為時間複雜度的原因,diff過程隻針對同層的節點作比較;
2.對于同類型的元件,才有進一步對比的必要性;
3.對于清單元件,通過key屬性來維持節點的穩定性,避免總是生産新節點;
複制代碼           

10. setState的工作流是怎麼樣的?

非并發(concurrent)模式:setState會出現異步和同步的現象。在生命周期和合成事件中是同步,而在setTimeout、setInterval、DOM原生函數等函數中是同步的。那麼這是為什麼尼?在合成事件或生命周期執行時,批量更新的任務鎖就被開啟了,我們所做的setState操作會被放入到批量更新隊列中,直到函數執行完,批量更新的任務鎖才會被關閉。批量更新的任務鎖是一個同步操作,而一旦你在setTimeout函數使用setState,此時setTimeout函數回調會被放入下一個宏任務執行,而當setState執行時,批量更新的任務鎖時關閉的,它就不會放入到批量更新隊列中,而是直接執行。

并發(concurrent)模式:setState不會出現異步和同步的現象。因為存在時間切片,隻要目前時間片沒有結束,依舊可以将多個 setState 合并成一個,即使是在setTimeout中被調用。而對于超過目前時間片的操作,會通過MessageChannel放入到下一個宏任務中繼續執行。(MessageChannel接收消息的時機比 Promise 所在的 microTask 要晚,但是早于 setTimeout)

11. Stack Reconciler棧調和 有怎麼樣的局限性?

浏覽器中Js線程和渲染線程是互斥的。這兩個線程不能穿插執行,必須串行。而當Js線程長時間占用主線程,那麼渲染線程的更新就不得不長時間的等待,這時就會導緻頁面卡頓。

Stack Reconciler棧調和是一個同步遞歸過程,虛拟DOM樹diff算法周遊是深度優先周遊。由于它是同步的,不可在被打斷。當處理結構複雜,體量龐大的虛拟DOM樹時,Stack Reconciler時間會很長,以為這Js主線程長時間占用主線程,進而導緻上述中說道的渲染卡頓/頁面卡死。

12. 說一說Fiber架構?

特點:可中斷、可恢複、存在優先級。

Scheduler ————> Reconciler ————> Renderer
 更新優先級         找不同         渲染不同
複制代碼           

在Fiber架構模式下,每個更新任務會被賦予一個優先級。當然有任務A進入排程器,這個任務優先級更高,而Reconciler中已有任務B在執行,那麼,Reconciler會将任務B終止,更高優先級的任務A被推入Reconciler。當A任務完成之後,新一輪排程會将之前中斷的任務B重新推入Reconciler,繼續它的渲染之旅。

render開始 ——————> (工作單元| 工作單元 | 工作單元) ——————> commit送出渲染
複制代碼           

13. ReactDOM.render調用棧的初始化階段、render階段

初始化階段:會建立root對象這個對象挂載_internalRoot屬性,而_internalRoot也就是FiberRoot。FiberRoot的本質是一個FiberRootNode對象,其中包含current屬性,current對象是一個FiberNode執行個體。current對象就是一個Fiber節點,并是Fiber樹的頭部節點;确定Fiber的優先級,結合優先級建立目前Fiber的update對象,并将其入隊排程FiberRoot;接下來進入render階段;(此時相當于隻有一個Fiber頭部節點)

render階段:通過createWorkInProgress函數,建立rootFiber節點的副本workInProgress節點樹(即current節點的副本節點),他們通過alternate互相引用;接着會觸發beginWork函數,進而實作對新的Fiber節點的建立。循環周遊,元件元素Fiber會不斷被建立(每個元素節點對應一個Fiber節點),直到建立到最後一個為止,此時Fiber樹(單連結清單)基本完成;

重點: 此時已經周遊到了單連結清單的最底部節點,然後會由下自上的依次生成真實DOM節點,同時被它的父元件副作用鍊,這個副作用鍊也是一個單連結清單,直周遊到根節點,此時的根節點上的副作用鍊就包含的全部的DOM更新。那麼剩下的隻需要拿到root下的副作用鍊更新即可了。

參考:

修言 深入淺出搞定 React

React 架構的演變 - 從同步到異步

React 架構的演變 - 從遞歸到循環

React 架構的演變 - 更新機制

React 架構的演變 - Hooks 的實作

繼續閱讀