2018年6月前端面試經歷(下)

我母雞啊!發表於2018-07-11

前言

這是6月前端面試最後一篇文章了,因為我的技術棧是react,下面都是面試官面對面問的一些問題的記錄~

react

react的生命週期

MOUNTING:

  • mountComponent 負責管理生命週期中的 getInitialState、componentWillMount、render 和 componentDidMount。

由於 getDefaultProps 是通過建構函式進行管理的,所以也是整個生命週期中最先開始執行的。 而 mountComponent 只能望洋興嘆, 無法呼叫到 getDefaultProps。 這就解釋了為何 getDefault-Props只執行一次。

RECEIVE_PROPS:

  • updateComponent 負責管理生命週期中的 componentWillReceiveProps、shouldComponent- Update、componentWillUpdate、render 和 componentDidUpdate。

UNMOUNTING:

  • unmountComponent 負責管理生命週期中的 componentWillUnmount

元件的生命週期在不同狀態下的執行順序。

  • 當首次掛載元件時, 按順序執行 getDefaultProps、 getInitialState、 componentWillMount、render 和 componentDidMount。
  • 當解除安裝元件時,執行 componentWillUnmount。
  • 重新掛載元件時,此時按順序執行 getInitialState、componentWillMount、render 和componentDidMount,但並不執行 getDefaultProps
  • 再次渲染元件時,元件接受到更新狀態,此時按順序執行 componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render 和 componentDidUpdate

react的生命週期與setState的關係

  • setState React 初學者常會寫出 this.state.value = 1 這樣的程式碼,這是完全錯誤的寫法。

絕對不要直接修改 this.state,這不僅是一種低效的做法,而且很有可能會被之後的操作替換

setState 通過一個佇列機制實現 state 更新。當執行 setState 時,會將需要更新的 state 合併後放入狀態佇列,而不會立刻更新 this.state,佇列機制可以高效地批量更新 state。如果不通過 setState 而直接修改 this.state 的值,那麼該 state 將不會被放入狀態佇列中,當下次呼叫 setState 並對狀態佇列進行合併時,將會忽略之前直接被修改的 state,而造成無法預知的錯誤。

因此,應該使用 setState 方法來更新 state,同時 React 也正是利用狀態佇列機制實現了 setState 的非同步更新,避免頻繁地重複更新 state。

相關原始碼如下:

// 將新的 state 合併到狀態更新佇列中 
var nextState = this._processPendingState(nextProps, nextContext); 
// 根據更新佇列和 shouldComponentUpdate 的狀態來判斷是否需要更新元件
var shouldUpdate =  this._pendingForceUpdate ||
                    !inst.shouldComponentUpdate ||
                    inst.shouldComponentUpdate(nextProps, nextState, nextContext)

複製程式碼
  • setState 在生命週期中的呼叫

WechatIMG2.jpeg-243.2kB

  • 如果我們在 componentWillMount 中執行 setState 方法,會發生什麼呢? 元件會更新 state,但元件只渲染一次。因此,這是無意義的執行,初始化時的 state 都可以放在 this.state。

  • 如果我們在 componentDidMount 中執行 setState 方法,又會發生什麼呢? 元件當然會再次更新,不過在初始化過程就渲染了兩次元件,這並不是一件好事。但實際情況是,有一些場景不得不需要 setState,比如計算元件的位置或寬高時,就不得不讓元件先渲染,更新必要的資訊後,再次渲染。

  • 如果我們在 componentWillUnmount 中執行 setState 方法,又會發生什麼呢? 不會觸發 re-render 的,這是因為所有更新佇列和更新狀態都被重置為null,並清除了公共類,完成了元件解除安裝操作

  • setState 迴圈呼叫風險

當呼叫 setState 時, 實際上會執行 enqueueSetState 方法, 並對 partialState 以及_pending- StateQueue 更新佇列進行合併操作,最終通過 enqueueUpdate 執行 state 更新。

而 performUpdateIfNecessary 方法會獲取 _pendingElement、_pendingStateQueue、_pending- ForceUpdate,並呼叫 receiveComponent 和 updateComponent 方法進行元件更新。

如果在 shouldComponentUpdatecomponentWillUpdate 方法中呼叫 setState,此時 this._pendingStateQueue != null,則 performUpdateIfNecessary 方法就會呼叫 updateComponent 方法進行元件更新,但 updateComponent 方法又會呼叫 shouldComponentUpdatecomponentWill- Update 方法,因此造成迴圈呼叫,使得瀏覽器記憶體佔滿後崩潰.如下圖

WechatIMG3.jpeg-77.2kB

pureComponent和component有什麼區別

在一個父元件裡有多個子元件的時候,修改一個子元件會導致所有子元件全部重新渲染。 我們一般都會使用shouldComponentUpdate的生命週期去判斷,但是這個生命週期是及其消耗效能的,在react裡並不推薦使用這個方法。

這個時候我們可以簡單的去模擬一個shouldComponentUpdate的方法。pureComponent就是在component的最外層幫我們預設實現了一個淺比較。

React.PureComponent 與 React.Component幾乎完全相同,但React.PureComponent通過prop和state的淺對比來實現 shouldComponentUpate()。

如果React元件的 render()函式在給定相同的props和state下渲染為相同的結果,在某些場景下你可以使用 React.PureComponent 來提升效能。

如果物件包含複雜的資料結構,它可能會因深層的資料不一致而產生錯誤的否定判斷(表現為物件深層的資料已改變檢視卻沒有更新)。

考慮使用不可變物件來促進巢狀資料的快速比較。此外,React.PureComponent的shouldComponentUpate() 會忽略整個元件的子級。請確保所有的子級元件也是”Pure”的

如何在在生命週期中傳送一個非同步的請求

一般在componentDidMount()生命週期裡去傳送一個非同步請求。因為這個時候可能需要的dom已經都掛載在了瀏覽器上,我們可以去拿到一些我們想要的引數,或者是把返回的資料儲存在標籤中。

參考一下官方對componentDidMount的解釋

componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request.

react的高階元件是什麼?如何實現一個高階元件?

React 元件的構建過程中,常常有這樣的場景,有一類功能需要被不同的元件公用,此時就涉及抽象的話題。在不同的設計理念下,有許多的抽象方法,而針對 React,我們會用到高階元件。

higher-order function(高階函式)在函數語言程式設計中是一個基本的概念,它描述的是這樣一種函式:這種函式接受函式作為輸入,或是輸出一個函式。比如,常用的工具方法 map、reduce 和 sort 等都是高階函式。 高階元件(higher-order component) ,類似於高階函式,它接受 React元件作為輸入,輸出一個新的 React 元件。 實現高階元件的方法有如下兩種:

  • 屬性代理(props proxy)。高階元件通過被包裹的 React 元件來操作 props。
  • 反向繼承(inheritance inversion)。高階元件繼承於被包裹的 React 元件。

從react推薦元件定義key的原因。

diff 作為 Virtual DOM 的加速器,其演算法上的改進優化是 React 整個介面渲染的基礎和效能保障, 同時也是 React 原始碼中最神祕、 最不可思議的部分。 本節依然從原始碼入手, 深入剖析 diff 的不可思議之處。

下面介紹 React diff 演算法的 3 個策略

  • 策略一:Web UI 中 DOM 節點跨層級的移動操作特別少,可以忽略不計
  • 策略二:擁有相同類的兩個元件將會生成相似的樹形結構,擁有不同類的兩個元件將會生成不同的樹形結構。
  • 策略三:對於同一層級的一組子節點,它們可以通過唯一 id 進行區分。

其中策略三就是:element diff,當節點處於同一層級時,diff 提供了 3 種節點操作,分別為 INSERT_MARKUP(插入) 、MOVE_ EXISTING(移動)和 REMOVE_NODE(刪除) 。

列如:舊集合中包含節點A、B、C 和 D,更新後的新集合中包含節點 B、A、D 和 C,此時新舊集合進行 diff 差異化對比,發現 B != A,則建立並插入 B 至新集合,刪除舊集合 A;以此類推,建立並插入 A、D 和 C,刪除 B、C 和 D。

WechatIMG4.jpeg-53.3kB

針對一些都是相同的節點,但由於位置發生變化,導致需要進行繁雜低效的刪除、建立操作,其實只要對這些節點進行位置移動即可。React 提出優化策略: 允許開發者對同一層級的同組子節點, 新增唯一 key 進行區分,雖然只是小小的改動,效能上卻發生了翻天覆地的變化!

新舊集合所包含的節點如圖所示,進行diff差異化對比後,通過key發現新舊集合中的節點都是相同的節點, 因此無需進行節點刪除和建立, 只需要將舊集合中節點的位置進行移動,更新為新集合中節點的位置,此時 React 給出的 diff 結果為:B、D 不做任何操作,A、C 進行移動操作即可。

WechatIMG5.jpeg-71.1kB

redux

redux裡combineReducers

combineReducers 輔助函式的作用是,把一個由多個不同 reducer 函式作為 value 的 object,合併成一個最終的 reducer 函式,然後就可以對這個 reducer 呼叫 createStore。

合併後的 reducer 可以呼叫各個子 reducer,並把它們的結果合併成一個 state 物件。state 物件的結構由傳入的多個 reducer 的 key 決定。

最終,state 物件的結構會是這樣的:

{
  reducer1: ...
  reducer2: ...
}
複製程式碼

通過為傳入物件的 reducer 命名不同來控制 state key 的命名。例如,你可以呼叫 combineReducers({ todos: myTodosReducer, counter: myCounterReducer }) 將 state 結構變為 { todos, counter }。

通常的做法是命名 reducer,然後 state 再去分割那些資訊,因此你可以使用 ES6 的簡寫方法:combineReducers({ counter, todos })。這與 combineReducers({ counter: counter, todos: todos }) 一樣。

Flux 使用者使用須知 本函式可以幫助你組織多個 reducer,使它們分別管理自身相關聯的 state。類似於 Flux 中的多個 store 分別管理不同的 state。在 Redux 中,只有一個 store,但是 combineReducers 讓你擁有多個 reducer,同時保持各自負責邏輯塊的獨立性。

  • 引數 reducers (Object): 一個物件,它的值(value) 對應不同的 reducer 函式,這些 reducer 函式後面會被合併成一個。下面會介紹傳入 reducer 函式需要滿足的規則。

  • 返回值 (Function):一個呼叫 reducers 物件裡所有 reducer 的 reducer,並且構造一個與 reducers 物件結構相同的 state 物件。

注意 本函式設計的時候有點偏主觀,就是為了避免新手犯一些常見錯誤。也因些我們故意設定一些規則,但如果你自己手動編寫根 redcuer 時並不需要遵守這些規則。

每個傳入 combineReducers 的 reducer 都需滿足以下規則:

所有未匹配到的 action,必須把它接收到的第一個引數也就是那個 state 原封不動返回。

永遠不能返回 undefined。當過早 return 時非常容易犯這個錯誤,為了避免錯誤擴散,遇到這種情況時 combineReducers 會拋異常。

如果傳入的 state 就是 undefined,一定要返回對應 reducer 的初始 state。根據上一條規則,初始 state 禁止使用 undefined。使用 ES6 的預設引數值語法來設定初始 state 很容易,但你也可以手動檢查第一個引數是否為 undefined。

雖然 combineReducers 自動幫你檢查 reducer 是否符合以上規則,但你也應該牢記,並儘量遵守。

//模擬一個combineReducers
var combineReducers1 = function(obj){
    //內部具體程式碼

    var finalState = {};
    function reducer(state,action){
        //reducer具體邏輯

        for (var p in obj) {
         //根據key屬性值呼叫function(state.屬性名,action)
         finalState[p] = obj[p](state[p], action);
        }

        //返回state
        return finalState;
    }

    //返回最終的reducer
    return reducer;
}
複製程式碼

redux的middleware的理解

面對多樣的業務場景,單純地修改 dispatch 或 reducer 的程式碼顯然不具有普適性,我們需要的是可以組合的、自由插拔的外掛機制,這一點 Redux 借鑑了 Koa (它是用於構建 Web 應用的 Node.js 框架)裡 middleware 的思想。

Redux 中 reducer 更關心的是資料的轉化邏輯,所以 middleware 就是為了增強 dispatch 而出現的。

WechatIMG6.jpeg-69.6kB

這裡是middleware實現的原始碼

export default function applyMiddleware(...middlewares) {
    return (next) => (reducer, initialState) => {
        let store = next(reducer, initialState);
        let dispatch = store.dispatch;
        let chain = [];
        var middlewareAPI = {
            getState: store.getState,
            dispatch: (action) => dispatch(action),
        };    
        chain = middlewares.map(middleware => middleware(middlewareAPI));
        dispatch = compose(...chain)(store.dispatch);
    return {
        ...store,
        dispatch,
        };
    }
}
複製程式碼

原始碼我就不解析了,這個原始碼我少說看了5遍,一直理解都不到位,精髓難懂啊

WechatIMG7.jpeg-39.2kB


彩蛋

webpack和gulp的區別

webpack的打包原理是:1.一切皆是模組。給定一個主體檔案(列如index.js),然後從這個主js中開始去招所有依賴的js,css,img等通過loader把他們都解析成模組輸出。

與gulp有什麼區別。

因為用過gulp,他的配置是任務,列task,比如js壓縮,解析。類似於對less或sass的編譯,組合,對圖片的壓縮,gulp就是提供一個流程化的一個工具。還可以在webpack前先執行gulp做一些流程化的配置,然後用gulp把webpack跑起來~

介紹幾款webpack的外掛

  • copy-webpack-plugin:將單個或整個目錄複製到構建目錄裡
  • Extract-text-webpack-plugin:打包的時候分離出文字,打包到單獨的檔案裡

在工作中遇到那些問題?如何解決的。

這個問題基本上是百分之百必問,我覺得,可以在自己的專案裡找一個即記憶猶新又能突出難點並且自己掌握的問題去說明~ 太簡單又突出不了各位的實力,太難了還沒掌握的面試官一深挖就暴露了,哈哈哈哈

非常感謝 《深入React技術棧》作者:陳屹 這本書,讓我在react的道路真的學到了很多~推薦~

友情連結:

2018年6月前端面試經歷(上)

2018年6月前端面試經歷(中)

相關文章