對React、Redux、React-Redux詳細剖析

段亦心發表於2018-06-25

前言

相信很多新手朋友們對於React、Redux、React-Redux這三者之間的關係和區別肯定有很多不解和疑惑。這裡我們就來詳細的剖析一下它們吧。

React:負責元件的UI介面渲染;
Redux:資料處理中心;
React-Redux:連線元件和資料中心,也就是把React和Redux聯絡起來。

React

React主要就是用來實現UI介面的,是一個專注於view層的框架。對於一些小專案,如果資料的互動不是很多,完全可以只使用React就能很好的實現。

在傳統的頁面開發模式中,需要多次的操作DOM來進行頁面的更新,我們都知道對DOM的操作會造成極大的效能問題。而React的提出就是減少對DOM的操作來提升效能,也就是Virtual DOM。

Virtual DOM

Virtual DOM就相當於一個虛擬空間,React就是基於 Virtual DOM 來工作的。

它的工作過程是:當有資料需要進行更新時,會先計算 Virtual DOM ,並和上一次的 Virtual DOM 做對比,得到DOM結構的區別,然後只會將需要變化的部分批量的更新到真實的DOM上。

說到如何去計算Virtual DOM,在React裡面,用到的是react-diff演算法。我們都知道傳統的diff演算法是通過迴圈遞迴對每個節點進行依次對比,效率低下,演算法複雜度達到了 O(n^3),其中 n 是樹中節點的總數。

根據react diff策略:

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

React 分別對 tree diff、component diff 以及 element diff 進行了演算法優化:

  1. tree diff:對樹進行分層比較,兩棵樹只會對同一層次的節點進行比較
  2. component diff
  • 如果是同一型別的元件,按照原策略繼續比較 virtual DOM tree。
  • 如果不是,則將該元件判斷為 dirty component,從而替換整個元件下的所有子節點。
  • 對於同一型別的元件,有可能其 Virtual DOM 沒有任何變化,如果能夠確切的知道這點那可以節省大量的 diff 運算時間,因此 React 允許使用者通過 shouldComponentUpdate() 來判斷該元件是否需要進行 diff。
  1. element diff:當節點處於同一層級時,React diff 提供了三種節點操作,分別為:插入、移動、刪除。

這是整個react diff演算法的比較流程圖:

react diff

React生命週期

React總共有10個周期函式(render重複一次),這10個函式可以滿足我們所有對元件操作的需求,利用的好可以提高開發效率和元件效能。

一、元件在初始化時會觸發5個鉤子函式:

  1. getDefaultProps()

設定預設的props,es6中用 static dufaultProps={} 設定元件的預設屬性。在整個生命週期只執行一次。

  1. getInitialState()

在使用es6的class語法時是沒有這個鉤子函式的,可以直接在constructor中定義this.state。此時可以訪問this.props。

  1. componentWillMount() ajax資料的拉取操作,定時器的啟動。

元件初始化時呼叫,以後元件更新不呼叫,整個生命週期只呼叫一次,此時可以修改state。

  1. render()

React最重要的步驟,建立虛擬dom,進行diff演算法,更新dom樹都在此進行。此時就不能更改state了。

  1. componentDidMount() 動畫的啟動,輸入框自動聚焦

元件渲染之後呼叫,可以通過this.getDOMNode()獲取和操作dom節點,只呼叫一次。

二、在更新時也會觸發5個鉤子函式:

  1. componentWillReceivePorps(nextProps)

元件初始化時不呼叫,元件接受新的props時呼叫。不管父元件傳遞給子元件的props有沒有改變,都會觸發。

  1. shouldComponentUpdate(nextProps, nextState)

React效能優化非常重要的一環。元件接受新的state或者props時呼叫,我們可以設定在此對比前後兩個props和state是否相同,如果相同則返回false阻止更新,因為相同的屬性狀態一定會生成相同的dom樹,這樣就不需要創造新的dom樹和舊的dom樹進行diff演算法對比,節省大量效能,尤其是在dom結構複雜的時候。不過呼叫this.forceUpdate會跳過此步驟。

  1. componentWillUpdate(nextProps, nextState)

元件初始化時不呼叫,只有在元件將要更新時才呼叫,此時可以修改state

  1. render()

不多說

  1. componentDidUpdate()

元件初始化時不呼叫,元件更新完成後呼叫,此時可以獲取dom節點。

三、解除安裝鉤子函式

  1. componentWillUnmount() 定時器的清除

元件將要解除安裝時呼叫,一些事件監聽和定時器需要在此時清除。

對React、Redux、React-Redux詳細剖析

Redux

Redux是一種架構模式,是由flux發展而來的。

Redux三大原則

  1. 唯一資料來源
  2. 狀態只讀
  3. 資料改變只能通過純函式(reducer)完成

Redux核心api

Redux主要由三部分組成:store,reducer,action。

store

Redux的核心是store,它由Redux提供的 createStore(reducer, defaultState) 這個方法生成,生成三個方法,getState(),dispatch(),subscrible()。

store

  • getState():儲存的資料,狀態樹;
  • dispatch(action):分發action,並返回一個action,這是唯一能改變store中資料的方式;
  • subscrible(listener):註冊一個監聽者,store發生變化的時候被呼叫。

reducer

reducer是一個純函式,它根據previousState和action計算出新的state。
reducer(previousState,action)

reducer

action

action本質上是一個JavaScript物件,其中必須包含一個type欄位來表示將要執行的動作,其他的欄位都可以根據需求來自定義。

const ADD_TODO = 'ADD_TODO'
複製程式碼
{
  type: ADD_TODO,
  text: 'Build my first Redux app'
}
複製程式碼

整合

他們三者之間的互動,可以由下圖概括:

對React、Redux、React-Redux詳細剖析

React-Redux

Redux 本身和React沒有關係,只是資料處理中心,是React-Redux讓他們聯絡在一起。

React-rRedux提供兩個方法:connect和Provider。

connect

connect連線React元件和Redux store。connect實際上是一個高階函式,返回一個新的已與 Redux store 連線的元件類。

const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)
複製程式碼

TodoList是 UI 元件,VisibleTodoList就是由 react-redux 通過connect方法自動生成的容器元件。

  1. mapStateToProps:從Redux狀態樹中提取需要的部分作為props傳遞給當前的元件。
  2. mapDispatchToProps:將需要繫結的響應事件(action)作為props傳遞到元件上。

對React、Redux、React-Redux詳細剖析

Provider

Provider實現store的全域性訪問,將store傳給每個元件。

原理:使用React的context,context可以實現跨元件之間的傳遞。

總結

下圖闡述了它們三者之間的工作流程:

對React、Redux、React-Redux詳細剖析

相關文章