開發中咱們並不能直接經過修改 state
的值來讓界面發生更新:react
state
以後, 但願 React
根據最新的 Stete
來從新渲染界面, 可是這種方式的修改 React
並不知道數據發生了變化React
並無實現相似於 Vue2
中的 Object.defineProperty
或者 Vue3
中的Proxy
的方式來監聽數據的變化setState
來告知 React
數據已經發生了變化疑惑: 在組件中並無實現 steState
方法, 爲何能夠調用呢?git
setState
方法是從 Component
中繼承過來的
<details>
<summary>setState是異步更新的</summary>
<img src="https://gitee.com/xmkm/cloudPic/raw/master/img/20201001214531.png" />
</details>github
爲何setState
設計爲異步呢?算法
setState
設計爲異步其實以前在 GitHub
上也有不少的討論簡單的總結: setState
設計爲異步, 能夠顯著的提升性能性能優化
setState
都進行一次更新, 那麼意味着 render
函數會被頻繁的調用界面從新渲染, 這樣的效率是很低的若是同步更新了 state
, 但尚未執行 render
函數, 那麼state
和props
不能保持同步異步
state
和props
不能保持一致性, 會在開發中產生不少的問題setState
異步更新state
後的值?方式一: setState
的回調函數
setState
接收兩個參數: 第二個參數是回調函數(callback
), 這個回調函數會在state
更新後執行
componentDidUpdate
生命週期函數
- 其實能夠分紅兩種狀況
- 在組件生命週期或React合成事件中,
setState
是異步的- 在
setTimeou
或原生DOM事件中,setState
是同步的
setTimeout
中的更新 —> 同步更新
DOM
事件 —> 同步更新
經過setState
去修改message
,是不會對其餘 state
中的數據產生影響的源碼分析
setState
, 只會生效最後一次state
setState
合併時進行累加: 給setState傳遞函數, 使用前一次state
中的值
React
的渲染流程:
React
在 props
或 state
發生改變時,會調用 React
的 render
方法,會建立一顆不一樣的樹React
須要基於這兩顆不一樣的樹之間的差異來判斷如何有效的更新UI
:若是一棵樹參考另一棵樹進行徹底比較更新, 那麼即便是最早進的算法, 該算法的複雜程度爲 O(n$^3$),其中 n 是樹中元素的數量性能
React
中使用了該算法, 那麼展現 1000
個元素所須要執行的計算量將在十億
的量級範圍因而,React
對這個算法進行了優化,將其優化成了O(n)
,如何優化的呢?學習
當節點爲不一樣的元素,React會拆卸原有的樹,而且創建起新的樹:
<a>
變成 <img>
,從 <Article>
變成 <Comment>
,或從 <button>
變成 <div>
都會觸發一個完整的重建流程DOM
節點也會被銷燬,組件實例將執行 componentWillUnmount()
方法DOM
節點會被建立以及插入到 DOM
中,組件實例將執行 componentWillMount()
方法,緊接着 componentDidMount()
方法好比下面的代碼更改:
好比下面的代碼更改:
React
知道只須要修改 DOM
元素上的 className
屬性
好比下面的代碼更改:
style
屬性時,React
僅更新有所改變的屬性。React
知道只須要修改 DOM
元素上的 color
樣式,無需修改 fontWeight
若是是同類型的組件元素:
React
會更新該組件的props
,而且調用componentWillReceiveProps()
和 componentWillUpdate()
方法render()
方法,diff
算法將在以前的結果以及新的結果中進行遞歸在默認條件下,當遞歸 DOM
節點的子元素時,React
會同時遍歷兩個子元素的列表;當產生差別時,生成一個 mutation
咱們來看一下在最後插入一條數據的狀況:👇
可是若是咱們是在前面插入一條數據:
<li>星際穿越</li>
和 <li>盜夢空間</li>
的不變key
屬性:
方式一:在<font color='red'>最後</font>位置插入數據
key
意義並不大方式二:在<font color='red'>前面</font>插入數據
key
的狀況下,全部的<li>
都須要進行修改在下面案例: 當子元素 (這裏的li
元素) 擁有 key
時
React
使用 key
來匹配原有樹上的子元素以及最新樹上的子元素:key
爲 333
的元素插入到最前面的位置便可
key
的注意事項:
key
應該是惟一的key
不要使用隨機數(隨機數在下一次render時,會從新生成一個數字)- 使用
index
做爲key
,對性能是沒有優化的
咱們使用以前的一個嵌套案例:
當點擊 +1
時,會從新調用 App
的 render
函數
那麼,咱們能夠思考一下,在之後的開發中,咱們只要是修改 了App中的數據,全部的子組件都須要從新render
,進行 diff
算法,性能必然是很低的:
render
render
方法如何來控制 render
方法是否被調用呢?
shouldComponentUpdate
方法便可React
給咱們提供了一個生命週期方法shouldComponentUpdate
(不少時候,咱們簡稱爲SCU
),這個方法接受參數,而且須要有返回值;主要做用是: 控制當前類組件對象是否調用render
方法
該方法有兩個參數:
nextProps
修改以後, 最新的 porps
屬性nextState
修改以後, 最新的 state
屬性該方法返回值是一個 booolan 類型
true
, 那麼就須要調用 render
方法false
, 那麼不須要調用 render
方法好比咱們在App中增長一個message
屬性:
JSX
中並沒有依賴這個message
, 那麼它的改變不該該引發從新渲染setState
修改 state
中的值, 因此最後 render
方法仍是被從新調用了// 決定當前類組件對象是否調用render方法 // 參數一: 最新的props // 參數二: 最新的state shouldComponentUpdate(nextProps, nextState) { // 默認是: return true // 不須要在頁面上渲染則不調用render函數 return false }
若是全部的類, 咱們都須要手動來實現 shouldComponentUpdate
, 那麼會給咱們開發者增長很是多的工做量
shouldComponentUpdate
中的各類判斷目的是什麼?props
或者 state
中數據是否發生了改變, 來決定shouldComponentUpdate
返回 true
或 false
事實上 React
已經考慮到了這一點, 因此 React
已經默認幫咱們實現好了, 如何實現呢?
state
和 porps
, 若是組件內沒有依賴 porps
或state
將不會調用render
state
或props
, 但卻調用了render
函數
這個方法中,調用
!shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
,這個shallowEqual
就是進行淺層比較:
render
: 在沒有依賴 state
或 props
但卻從新渲染 render
問題咱們須要使用一個高階組件memo
:
counter
發生改變時,Header、Banner、ProductList的函數不會從新執行,而 Footer 的函數會被從新執行import React, { PureComponent, memo } from 'react' // MemoHeader: 沒有依賴props,不會被從新調用render渲染 const MemoHeader = memo(function Header() { console.log('Header被調用') return <h2>我是Header組件</h2> })