2019年17道高頻React面試題及詳解

如下面試題來源於github項目前端面試指南,那裏有超過200道高頻前端面試題及答案,目前擁有1400star.前端

爲何選擇使用框架而不是原生?

框架的好處:react

  1. 組件化: 其中以 React 的組件化最爲完全,甚至能夠到函數級別的原子組件,高度的組件化能夠是咱們的工程易於維護、易於組合拓展。
  2. 自然分層: JQuery 時代的代碼大部分狀況下是麪條代碼,耦合嚴重,現代框架無論是 MVC、MVP仍是MVVM 模式都能幫助咱們進行分層,代碼解耦更易於讀寫。
  3. 生態: 如今主流前端框架都自帶生態,無論是數據流管理架構仍是 UI 庫都有成熟的解決方案。
  4. 開發效率: 現代前端框架都默認自動更新DOM,而非咱們手動操做,解放了開發者的手動DOM成本,提升開發效率,從根本上解決了UI 與狀態同步問題.

虛擬DOM的優劣如何?

優勢:git

  • 保證性能下限: 虛擬DOM能夠通過diff找出最小差別,而後批量進行patch,這種操做雖然比不上手動優化,可是比起粗暴的DOM操做性能要好不少,所以虛擬DOM能夠保證性能下限
  • 無需手動操做DOM: 虛擬DOM的diff和patch都是在一次更新中自動進行的,咱們無需手動操做DOM,極大提升開發效率
  • 跨平臺: 虛擬DOM本質上是JavaScript對象,而DOM與平臺強相關,相比之下虛擬DOM能夠進行更方便地跨平臺操做,例如服務器渲染、移動端開發等等

缺點:程序員

  • 沒法進行極致優化: 在一些性能要求極高的應用中虛擬DOM沒法進行鍼對性的極致優化,好比VScode採用直接手動操做DOM的方式進行極端的性能優化

虛擬DOM實現原理?

  • 虛擬DOM本質上是JavaScript對象,是對真實DOM的抽象
  • 狀態變動時,記錄新樹和舊樹的差別
  • 最後把差別更新到真正的dom中

虛擬DOM原理github

React最新的生命週期是怎樣的?

React 16以後有三個生命週期被廢棄(但並未刪除)面試

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

官方計劃在17版本徹底刪除這三個函數,只保留UNSAVE_前綴的三個函數,目的是爲了向下兼容,可是對於開發者而言應該儘可能避免使用他們,而是使用新增的生命週期函數替代它們算法

目前React 16.8 +的生命週期分爲三個階段,分別是掛載階段、更新階段、卸載階段npm

掛載階段:編程

  • constructor: 構造函數,最早被執行,咱們一般在構造函數裏初始化state對象或者給自定義方法綁定this
  • getDerivedStateFromProps: static getDerivedStateFromProps(nextProps, prevState),這是個靜態方法,當咱們接收到新的屬性想去修改咱們state,可使用getDerivedStateFromProps
  • render: render函數是純函數,只返回須要渲染的東西,不該該包含其它的業務邏輯,能夠返回原生的DOM、React組件、Fragment、Portals、字符串和數字、Boolean和null等內容
  • componentDidMount: 組件裝載以後調用,此時咱們能夠獲取到DOM節點並操做,好比對canvas,svg的操做,服務器請求,訂閱均可以寫在這個裏面,可是記得在componentWillUnmount中取消訂閱

更新階段:redux

  • getDerivedStateFromProps: 此方法在更新個掛載階段均可能會調用
  • shouldComponentUpdate: shouldComponentUpdate(nextProps, nextState),有兩個參數nextProps和nextState,表示新的屬性和變化以後的state,返回一個布爾值,true表示會觸發從新渲染,false表示不會觸發從新渲染,默認返回true,咱們一般利用今生命週期來優化React程序性能
  • render: 更新階段也會觸發今生命週期
  • getSnapshotBeforeUpdate: getSnapshotBeforeUpdate(prevProps, prevState),這個方法在render以後,componentDidUpdate以前調用,有兩個參數prevProps和prevState,表示以前的屬性和以前的state,這個函數有一個返回值,會做爲第三個參數傳給componentDidUpdate,若是你不想要返回值,能夠返回null,今生命週期必須與componentDidUpdate搭配使用
  • componentDidUpdate: componentDidUpdate(prevProps, prevState, snapshot),該方法在getSnapshotBeforeUpdate方法以後被調用,有三個參數prevProps,prevState,snapshot,表示以前的props,以前的state,和snapshot。第三個參數是getSnapshotBeforeUpdate返回的,若是觸發某些回調函數時須要用到 DOM 元素的狀態,則將對比或計算的過程遷移至 getSnapshotBeforeUpdate,而後在 componentDidUpdate 中統一觸發回調或更新狀態。

卸載階段:

  • componentWillUnmount: 當咱們的組件被卸載或者銷燬了就會調用,咱們能夠在這個函數裏去清除一些定時器,取消網絡請求,清理無效的DOM元素等垃圾清理工做

2019-07-31-14-30-17

一個查看react生命週期的網站

React的請求應該放在哪一個生命週期中?

React的異步請求到底應該放在哪一個生命週期裏,有人認爲在componentWillMount中能夠提早進行異步請求,避免白屏,其實這個觀點是有問題的.

因爲JavaScript中異步事件的性質,當您啓動API調用時,瀏覽器會在此期間返回執行其餘工做。當React渲染一個組件時,它不會等待componentWillMount它完成任何事情 - React繼續前進並繼續render,沒有辦法「暫停」渲染以等待數據到達。

並且在componentWillMount請求會有一系列潛在的問題,首先,在服務器渲染時,若是在 componentWillMount 裏獲取數據,fetch data會執行兩次,一次在服務端一次在客戶端,這形成了多餘的請求,其次,在React 16進行React Fiber重寫後,componentWillMount可能在一次渲染中屢次調用.

目前官方推薦的異步請求是在componentDidmount中進行.

若是有特殊需求須要提早請求,也能夠在特殊狀況下在constructor中請求:

react 17以後componentWillMount會被廢棄,僅僅保留UNSAFE_componentWillMount

setState究竟是異步仍是同步?

先給出答案: 有時表現出異步,有時表現出同步

  1. setState只在合成事件和鉤子函數中是「異步」的,在原生事件和setTimeout 中都是同步的。
  2. setState 的「異步」並非說內部由異步代碼實現,其實自己執行的過程和代碼都是同步的,只是合成事件和鉤子函數的調用順序在更新以前,致使在合成事件和鉤子函數中無法立馬拿到更新後的值,造成了所謂的「異步」,固然能夠經過第二個參數 setState(partialState, callback) 中的callback拿到更新後的結果。
  3. setState 的批量更新優化也是創建在「異步」(合成事件、鉤子函數)之上的,在原生事件和setTimeout 中不會批量更新,在「異步」中若是對同一個值進行屢次setStatesetState的批量更新策略會對其進行覆蓋,取最後一次的執行,若是是同時setState多個不一樣的值,在更新時會對其進行合併批量更新。

React組件通訊如何實現?

React組件間通訊方式:

  • 父組件向子組件通信: 父組件能夠向子組件經過傳 props 的方式,向子組件進行通信
  • 子組件向父組件通信: props+回調的方式,父組件向子組件傳遞props進行通信,此props爲做用域爲父組件自身的函數,子組件調用該函數,將子組件想要傳遞的信息,做爲參數,傳遞到父組件的做用域中
  • 兄弟組件通訊: 找到這兩個兄弟節點共同的父節點,結合上面兩種方式由父節點轉發信息進行通訊
  • 跨層級通訊: Context設計目的是爲了共享那些對於一個組件樹而言是「全局」的數據,例如當前認證的用戶、主題或首選語言,對於跨越多層的全局數據經過Context通訊再適合不過
  • 發佈訂閱模式: 發佈者發佈事件,訂閱者監聽事件並作出反應,咱們能夠經過引入event模塊進行通訊
  • 全局狀態管理工具: 藉助Redux或者Mobx等全局狀態管理工具進行通訊,這種工具會維護一個全局狀態中心Store,並根據不一樣的事件產生新的狀態

2019-07-31-18-38-37

React有哪些優化性能是手段?

性能優化的手段不少時候是通用的詳情見前端性能優化加載篇

React如何進行組件/邏輯複用?

拋開已經被官方棄用的Mixin,組件抽象的技術目前有三種比較主流:

  • 高階組件:
    • 屬性代理
    • 反向繼承
  • 渲染屬性
  • react-hooks

組件複用詳解見組件複用

mixin、hoc、render props、react-hooks的優劣如何?

Mixin的缺陷:

  • 組件與 Mixin 之間存在隱式依賴(Mixin 常常依賴組件的特定方法,但在定義組件時並不知道這種依賴關係)

  • 多個 Mixin 之間可能產生衝突(好比定義了相同的state字段)

  • Mixin 傾向於增長更多狀態,這下降了應用的可預測性(The more state in your application, the harder it is to reason about it.),致使複雜度劇增

  • 隱式依賴致使依賴關係不透明,維護成本和理解成本迅速攀升:

    • 難以快速理解組件行爲,須要全盤瞭解全部依賴 Mixin 的擴展行爲,及其之間的相互影響

    • 組價自身的方法和state字段不敢輕易刪改,由於難以肯定有沒有 Mixin 依賴它

    • Mixin 也難以維護,由於 Mixin 邏輯最後會被打平合併到一塊兒,很難搞清楚一個 Mixin 的輸入輸出

HOC相比Mixin的優點:

  • HOC經過外層組件經過 Props 影響內層組件的狀態,而不是直接改變其 State不存在衝突和互相干擾,這就下降了耦合度
  • 不一樣於 Mixin 的打平+合併,HOC 具備自然的層級結構(組件樹結構),這又下降了複雜度

HOC的缺陷:

  • 擴展性限制: HOC 沒法從外部訪問子組件的 State所以沒法經過shouldComponentUpdate濾掉沒必要要的更新,React 在支持 ES6 Class 以後提供了React.PureComponent來解決這個問題
  • Ref 傳遞問題: Ref 被隔斷,後來的React.forwardRef 來解決這個問題
  • Wrapper Hell: HOC可能出現多層包裹組件的狀況,多層抽象一樣增長了複雜度和理解成本
  • 命名衝突: 若是高階組件屢次嵌套,沒有使用命名空間的話會產生衝突,而後覆蓋老屬性
  • 不可見性: HOC至關於在原有組件外層再包裝一個組件,你壓根不知道外層的包裝是啥,對於你是黑盒

Render Props優勢:

  • 上述HOC的缺點Render Props均可以解決

Render Props缺陷:

  • 使用繁瑣: HOC使用只須要藉助裝飾器語法一般一行代碼就能夠進行復用,Render Props沒法作到如此簡單
  • 嵌套過深: Render Props雖然擺脫了組件多層嵌套的問題,可是轉化爲了函數回調的嵌套

React Hooks優勢:

  • 簡潔: React Hooks解決了HOC和Render Props的嵌套問題,更加簡潔
  • 解耦: React Hooks能夠更方便地把 UI 和狀態分離,作到更完全的解耦
  • 組合: Hooks 中能夠引用另外的 Hooks造成新的Hooks,組合變化萬千
  • 函數友好: React Hooks爲函數組件而生,從而解決了類組件的幾大問題:
    • this 指向容易錯誤
    • 分割在不一樣聲明週期中的邏輯使得代碼難以理解和維護
    • 代碼複用成本高(高階組件容易使代碼量劇增)

React Hooks缺陷:

  • 額外的學習成本(Functional Component 與 Class Component 之間的困惑)

  • 寫法上有限制(不能出如今條件、循環中),而且寫法限制增長了重構成本

  • 破壞了PureComponent、React.memo淺比較的性能優化效果(爲了取最新的props和state,每次render()都要從新建立事件處函數)

  • 在閉包場景可能會引用到舊的state、props值

  • 內部實現上不直觀(依賴一份可變的全局狀態,再也不那麼「純」)

  • React.memo並不能徹底替代shouldComponentUpdate(由於拿不到 state change,只針對 props change)

關於react-hooks的評價來源於官方react-hooks RFC

你是如何理解fiber的?

React Fiber 是一種基於瀏覽器的單線程調度算法.

React 16以前 ,reconcilation 算法其實是遞歸,想要中斷遞歸是很困難的,React 16 開始使用了循環來代替以前的遞歸.

Fiber一種將 recocilation (遞歸 diff),拆分紅無數個小任務的算法;它隨時可以中止,恢復。中止恢復的時機取決於當前的一幀(16ms)內,還有沒有足夠的時間容許計算。

Fiber 詳解

你對 Time Slice的理解?

時間分片

  • React 在渲染(render)的時候,不會阻塞如今的線程
  • 若是你的設備足夠快,你會感受渲染是同步的
  • 若是你設備很是慢,你會感受還算是靈敏的
  • 雖然是異步渲染,可是你將會看到完整的渲染,而不是一個組件一行行的渲染出來
  • 一樣書寫組件的方式

也就是說,這是React背後在作的事情,對於咱們開發者來講,是透明的,具體是什麼樣的效果呢?

有圖表三個圖表,有一個輸入框,以及上面的三種模式
這個組件很是的巨大,並且在輸入框每次**輸入東西的時候,就會進去一直在渲染。**爲了更好的看到渲染的性能,Dan爲咱們作了一個表。

咱們先看看,同步模式:


同步模式下,咱們都知道,咱們沒輸入一個字符,React就開始渲染,當React渲染一顆巨大的樹的時候,是很是卡的,因此纔會有shouldUpdate的出現,在這裏Dan也展現了,這種卡!

咱們再來看看第二種(Debounced模式):


Debounced模式簡單的來講,就是延遲渲染,好比,當你輸入完成之後,再開始渲染全部的變化。
這麼作的壞處就是,至少不會阻塞用戶的輸入了,可是依然有很是嚴重的卡頓。

切換到異步模式:


異步渲染模式就是不阻塞當前線程,繼續跑。在視頻裏能夠看到全部的輸入,表上都會是原諒色的。

時間分片正是基於可隨時打斷、重啓的Fiber架構,可打斷當前任務,優先處理緊急且重要的任務,保證頁面的流暢運行.

redux的工做流程?

首先,咱們看下幾個核心概念:

  • Store:保存數據的地方,你能夠把它當作一個容器,整個應用只能有一個Store。
  • State:Store對象包含全部數據,若是想獲得某個時點的數據,就要對Store生成快照,這種時點的數據集合,就叫作State。
  • Action:State的變化,會致使View的變化。可是,用戶接觸不到State,只能接觸到View。因此,State的變化必須是View致使的。Action就是View發出的通知,表示State應該要發生變化了。
  • Action Creator:View要發送多少種消息,就會有多少種Action。若是都手寫,會很麻煩,因此咱們定義一個函數來生成Action,這個函數就叫Action Creator。
  • Reducer:Store收到Action之後,必須給出一個新的State,這樣View纔會發生變化。這種State的計算過程就叫作Reducer。Reducer是一個函數,它接受Action和當前State做爲參數,返回一個新的State。
  • dispatch:是View發出Action的惟一方法。

而後咱們過下整個工做流程:

  1. 首先,用戶(經過View)發出Action,發出方式就用到了dispatch方法。
  2. 而後,Store自動調用Reducer,而且傳入兩個參數:當前State和收到的Action,Reducer會返回新的State
  3. State一旦有變化,Store就會調用監聽函數,來更新View。

到這兒爲止,一次用戶交互流程結束。能夠看到,在整個流程中數據都是單向流動的,這種方式保證了流程的清晰。

2019-08-01-17-29-20

redux原理詳解

react-redux是如何工做的?

  • Provider: Provider的做用是從最外部封裝了整個應用,並向connect模塊傳遞store
  • connect: 負責鏈接React和Redux
    • 獲取state: connect經過context獲取Provider中的store,經過store.getState()獲取整個store tree 上全部state
    • 包裝原組件: 將state和action經過props的方式傳入到原組件內部wrapWithConnect返回一個ReactComponent對象Connect,Connect從新render外部傳入的原組件WrappedComponent,並把connect中傳入的mapStateToProps, mapDispatchToProps與組件上原有的props合併後,經過屬性的方式傳給WrappedComponent
    • 監聽store tree變化: connect緩存了store tree中state的狀態,經過當前state狀態和變動前state狀態進行比較,從而肯定是否調用this.setState()方法觸發Connect及其子組件的從新渲染

2019-08-01-22-21-51

redux與mobx的區別?

二者對比:

  • redux將數據保存在單一的store中,mobx將數據保存在分散的多個store中
  • redux使用plain object保存數據,須要手動處理變化後的操做;mobx適用observable保存數據,數據變化後自動處理響應的操做
  • redux使用不可變狀態,這意味着狀態是隻讀的,不能直接去修改它,而是應該返回一個新的狀態,同時使用純函數;mobx中的狀態是可變的,能夠直接對其進行修改
  • mobx相對來講比較簡單,在其中有不少的抽象,mobx更多的使用面向對象的編程思惟;redux會比較複雜,由於其中的函數式編程思想掌握起來不是那麼容易,同時須要藉助一系列的中間件來處理異步和反作用
  • mobx中有更多的抽象和封裝,調試會比較困難,同時結果也難以預測;而redux提供可以進行時間回溯的開發工具,同時其純函數以及更少的抽象,讓調試變得更加的容易

場景辨析:

基於以上區別,咱們能夠簡單得分析一下二者的不一樣使用場景.

mobx更適合數據不復雜的應用: mobx難以調試,不少狀態沒法回溯,面對複雜度高的應用時,每每力不從心.

redux適合有回溯需求的應用: 好比一個畫板應用、一個表格應用,不少時候須要撤銷、重作等操做,因爲redux不可變的特性,自然支持這些操做.

mobx適合短平快的項目: mobx上手簡單,樣板代碼少,能夠很大程度上提升開發效率.

固然mobx和redux也並不必定是非此即彼的關係,你也能夠在項目中用redux做爲全局狀態管理,用mobx做爲組件局部狀態管理器來用.

redux中如何進行異步操做?

固然,咱們能夠在componentDidmount中直接進行請求無須藉助redux.

可是在必定規模的項目中,上述方法很難進行異步流的管理,一般狀況下咱們會藉助redux的異步中間件進行異步處理.

redux異步流中間件其實有不少,可是當下主流的異步中間件只有兩種redux-thunk、redux-saga,固然redux-observable可能也有資格佔據一席之地,其他的異步中間件無論是社區活躍度仍是npm下載量都比較差了.

redux異步中間件之間的優劣?

redux-thunk優勢:

  • 體積小: redux-thunk的實現方式很簡單,只有不到20行代碼
  • 使用簡單: redux-thunk沒有引入像redux-saga或者redux-observable額外的範式,上手簡單

redux-thunk缺陷:

  • 樣板代碼過多: 與redux自己同樣,一般一個請求須要大量的代碼,並且不少都是重複性質的
  • 耦合嚴重: 異步操做與redux的action偶合在一塊兒,不方便管理
  • 功能孱弱: 有一些實際開發中經常使用的功能須要本身進行封裝

redux-saga優勢:

  • 異步解耦: 異步操做被被轉移到單獨 saga.js 中,再也不是摻雜在 action.js 或 component.js 中
  • action擺脫thunk function: dispatch 的參數依然是一個純粹的 action (FSA),而不是充滿 「黑魔法」 thunk function
  • 異常處理: 受益於 generator function 的 saga 實現,代碼異常/請求失敗 均可以直接經過 try/catch 語法直接捕獲處理
  • 功能強大: redux-saga提供了大量的Saga 輔助函數和Effect 建立器供開發者使用,開發者無須封裝或者簡單封裝便可使用
  • 靈活: redux-saga能夠將多個Saga能夠串行/並行組合起來,造成一個很是實用的異步flow
  • 易測試,提供了各類case的測試方案,包括mock task,分支覆蓋等等

redux-saga缺陷:

  • 額外的學習成本: redux-saga不只在使用難以理解的 generator function,並且有數十個API,學習成本遠超redux-thunk,最重要的是你的額外學習成本是隻服務於這個庫的,與redux-observable不一樣,redux-observable雖然也有額外學習成本可是背後是rxjs和一整套思想
  • 體積龐大: 體積略大,代碼近2000行,min版25KB左右
  • 功能過剩: 實際上併發控制等功能很難用到,可是咱們依然須要引入這些代碼
  • ts支持不友好: yield沒法返回TS類型

redux-observable優勢:

  • 功能最強: 因爲背靠rxjs這個強大的響應式編程的庫,藉助rxjs的操做符,你能夠幾乎作任何你能想到的異步處理
  • 背靠rxjs: 因爲有rxjs的加持,若是你已經學習了rxjs,redux-observable的學習成本並不高,並且隨着rxjs的升級redux-observable也會變得更強大

redux-observable缺陷:

  • 學習成本奇高: 若是你不會rxjs,則須要額外學習兩個複雜的庫
  • 社區通常: redux-observable的下載量只有redux-saga的1/5,社區也不夠活躍,在複雜異步流中間件這個層面redux-saga仍處於領導地位

關於redux-saga與redux-observable的詳細比較可見此連接


公衆號

想要實時關注筆者最新的文章和最新的文檔更新請關注公衆號程序員面試官,後續的文章會優先在公衆號更新.

簡歷模板: 關注公衆號回覆「模板」獲取

《前端面試手冊》: 配套於本指南的突擊手冊,關注公衆號回覆「fed」獲取

2019-08-12-03-18-41
相關文章
相關標籤/搜索