React踩坑筆記 —— 差分算法(一)

概要

  • 《React踩坑筆記 —— 差分算法(二)》

React提供了聲明式的API,以致於咱們不須要擔憂每次更新具體發生了什麼更改。這使得咱們開發應用變得很容易,但始終沒法清楚React內部是如何實現的。本文解釋了在 「差分算法」 中如何作出選擇,使得組件更新,在足夠快的高性能應用中仍然能夠預測。html

在理解 「差分算法」 以前,首先咱們須要去理解:node

  • document與Dom nodes的關係;
  • document與react組件的關係;
  • react組件與react元素樹的關係;
  • render()函數的做用與生命週期;
  • ReactDOM.render()函數的做用;

Document與React元素樹

JSX代碼

import React from "react";
import ReactDOM from 'react-dom';

const First = () => (<div> <h1>第一棵樹</h1> <div>附加信息</div> </div>);

const Second = () => (<div> <h1>第二棵樹</h1> <Third /> </div>);

const Third = () => (<div> <h1>第三棵樹</h1> <div>附加信息</div> </div>);

const App = () => (<div> <First /> <Second /> </div>);

ReactDOM.render(<App />, document.getElementById('root')); 複製代碼

圖示

在這裏插入圖片描述

說明

萬變不離其宗,對於一個HTML頁面,documentNode永遠會是它的基礎組成部分(實際上document也只是一個特別的Node),若是把document看作一張紙,那麼Node能夠當作點綴在紙上的文字、圖片或符號。react

在React中,React組件是由真實的Dom Node組成,最終會被轉化爲Dom Node並在document中渲染出來(事件\樣式\選擇器,只屬於真實的DOM Node)。算法

當你使用React的時候,你能夠認爲:瀏覽器

  • 每個React組件都對應一棵React元素樹
  • 或者說,在某一時間點上每個render()函數都建立了一棵React元素樹
  • 這裏的render()函數,見下文。
  • 每個React組件能夠由一個或多個React組件以及HTMLElement組成,便是 —— 每一棵樹均可以做爲另外一棵樹的子樹,正如上圖所示:<Third /><Second />的子樹,<Second /><First />又都是<App />的子樹。
  • 在上圖中,<App />組件所對應的React元素樹,是最大的一棵樹。經過API ——ReactDOM.render(element, container[, callback])將這棵樹掛載到容器(container)中。
  • 所謂容器(container),不過是document中某個指定的Div或其它塊級元素
  • 因此,不管經過JavaScript選擇器仍是dom操做插件仍是React提供的Refs,對Dom node的操做都會被保留在這個document中,並在組件stateprops發生改變的時候參與 「差分算法」
  • 因此,若是當前頁面被刷新,那麼原來的document也不復存在:
    • React組件會被從新掛載
    • Dom node會被從新插入
    • 若是你想保留某些狀態,你須要高於頁面的存儲方式,例如sessionStorage( 標籤頁 localStorage( 瀏覽器 cookie( 瀏覽器 Web SQL( 瀏覽器 服務器數據( 任何地方
  • 路由(Router),只會引發某些組件的卸載或掛載,而不會顛覆整個document。

render()

對於Function組件(無狀態組件)就是自身,對於Class組件(有狀態組件)就是從React.Component繼承的render()方法。服務器

返回值類型

render()被調用時,能夠依賴propsstate來構造返回值,其返回值類型包括以下:cookie

  • React elements. 典型地經過JSX語法建立,例如<div /><MyComponent />
  • Arrays and fragments. 因爲 「差分算法」 ,React要求render()必須返回單元素(single),React提供了<></><React.Fragment>容許開發者從render()中返回多元素
  • Portals. 有時咱們須要將子組件渲染到父組件外的某個Dom node上,例如對話框、懸浮卡、提示信息。Portals 爲咱們都提供了首選方式。
  • String and numbers. 字符串和數字被以文本節點(Text nodes)的形式渲染到Dom中
  • Booleans or null. 返回Null表示什麼都不作。 (大多數狀況是返回 test && <Child /> ,這裏test 是布爾值(boolean)

純函數

render()應該是一個純函數,返回值只依賴於stateprops,而且不會產生反作用,反作用包括:網絡

  • 修改stateprops
  • 調用非純函數。如 Date.now()Math.random()
  • 網絡請求,路由跳轉
  • 瀏覽器交互。setTimeout()console.log()

若是你須要瀏覽器交互網絡請求,能夠選擇在componentDidMount()其它合適的聲明周期函數中執行。有些條件沒有作硬性要求,可是保證render()做爲純函數,可以提升組件更新的性能。session

生命週期

組件在掛載(Mounting)和更新(Updating)時,render()都會被執行,併產生一棵新的React元素樹。聲明週期執行順序以下。詳情可見官方文檔《component-lifecycle》dom

Mounting

render()會產生一棵新的樹,這顆新樹會被直接掛載到父組件對應的那棵樹上。

  • constructor()
  • static getDerivedStateFromProps()
  • UNSAFE_componentWillMount()
  • render()
  • componentDidMount()

Updating

render()會產生一棵新的樹,而後與先前的那棵舊樹,執行 「 差分算法」 更新UI(用戶界面)。

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • UNSAFE_componentWillUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()

ReactDOM.render()

ReactDOM.render(element, container[, callback])
複製代碼

該方法將整個React元素樹渲染到document中指定的<Div />容器中,並返回根組件(對應最大元素樹)的引用(Function 組件 —— 無狀態組件,沒有實例(Instance)因此返回null)。

若是這個React元素(根組件)先前已經被渲染到容器中,那麼將對它執行更新,按照 「差分算法」 去呈現它。

若是這個可選的參數—— 回調函數,被提供。那麼會在組件被渲染更新後調用。

Note

  • ReactDOM.render()控制着容器(container)節點的內容,第一次調用時,任何存在的Dom元素都會被替換,以後再調用會使用React Dom diffing algorithm(差分算法)進行高效更新。
  • ReactDOM.render()不修改容器節點(只修改容器的子節點)。
  • ReactDOM.render() 當前返回根組件(root ReactComponent )實例的引用。然而,返回值的使用已經被遺留了,應該避免去使用它,由於在將來的React版本中,在某些狀況下React會採用異步的方式去渲染組件。若是你真的須要引用根組件實例,首選解決方案是爲你的根組件添加《callback ref》
  • 另外,使用ReactDOM.render()去融合服務端渲染容器(server-rendered container)已經被遺棄了,並將在React 17被移除,代替使用hydrate()
相關文章
相關標籤/搜索