重談react優點——react技術棧回顧

react剛剛推出的時候,講react優點搜索結果是幾十頁。javascript

如今,react已經慢慢退火,該用用react技術棧的已經使用上,填過多少坑,加過多少班,血淚控訴也不下千文。php

今天,再談一遍react優點,WTF?css

React的收益有哪些?React的優點是什麼?react和vue、angularJS等其它框架對比優點?html

 

而做爲總結回顧。react在工程實踐中,帶來哪些思想上的質變?前端

 

virtual dom虛擬DOM概念vue

它並不直接對DOM進行操做,引入了一個叫作virtual dom的概念,安插在javascript邏輯和實際的DOM之間,好處是減小DOM操做,減小DOM操做的目的是提升瀏覽器的渲染性能。java

虛擬dom就中小型項目而言,的確從表象上看不出太多的優點,由於它解決的是底層的dom渲染,IO開銷問題。可是想一想facebook的體量,不難猜出react的誕生是爲了解決更復雜更大型的項目開發和管理的。node

實際上React和Vue其實也在操做DOM,只是比較高效地在操做DOM而已,虛擬DOM其實最終也會映射到真實DOM,雖然虛擬DOM只會將變化的部分更新到真實DOM,但實際上直接操做DOM也能夠經過某些方式去優化,那麼:react

    一、操做data,不直接操做DOM有什麼好處?android

         更少的代碼作更多的事。

    二、操做data會給DOM操做帶來什麼很差的地方嗎?

          不會,可是不是全部功能「使用操做data」均可以代替的。

    三、會不會比直接操做DOM存在什麼難度?

         不會有難度,可是思惟須要有一些轉變。

 

JSX雖然作了抽象視圖,但她是聲明式API,可以保證你看一眼就知道組件樹的結構,譬如:

 

這結構還算清楚吧,基本一眼就知道這個一個面板由輸入框、列表、摘要組成,並且佈局也清楚了,自上而下。並且,經過查看一個源文件就能夠知道你的組件將會如何渲染。這是最大的好處,儘管這和 Angular 模板沒什麼不一樣。具體參看:ReactJS For Stupid People

 

以前寫UI的時候每每爲了性能,要設計不少DOM的操做邏輯,用了react以後,這些都不給你作了,由他的state跟props來傳遞給VDOM,很省事,更專一於UI層面。

 

學會了react以及這個JSX語法,你不光能夠經過react寫web;也能夠經過react-native寫ios或者android的應用;甚至能夠經過react-blessed寫terminal可視化應用;固然也能夠經過react-native-desktop寫桌面應用。由於JSX這種聲明式語法實際是在構建一個抽象的視圖層,這種抽象能夠經過不一樣適配器適配到各類顯示終端,這總夠屌吧?

 

unidirectional data flow-單向數據流

React倡導使用flux模式來進行組件間數據傳輸,這種作法叫unidirectional data flow(單向數據流),單向數據流的好處是與以前angularJS提出的two-way data binding相比較而言,由於單向,因此各類變化都是可預計、可控制的。不像two-way data binding那樣,變化一但複雜起來,你們都互相觸發變化,到最後一個地方變了,你根本猜不出來她還會致使其餘什麼地方跟着一塊兒變。這個須要大量實踐纔能有所感覺,若是你初學,那聽聽就算了,沒必要死磕。

 

react項目結構更加清晰:

virtual dom、redux、action,分部分別存放,就象java寫後臺查數據原本用jdbc一條sql就搞定,但分紅action service dao分門別類地存放,這樣維護性好,大公司的代碼須要規範,這樣出了問題好找緣由。

 

組件化

一切都是component:代碼更加模塊化,重用代碼更容易,可維護性高。

這裏就涉及到react的 架構,好比:

smart, dumb component  

把組件分紅兩大類 Smart Components (容器) & Dumb Components(顆粒化組件)

這樣作的好處:

  • 有助理你分離關注點,這樣的話更有助於理解你的app的業務邏輯 和 它的ui

  • 更有助於複用你的dumb組件,你能夠將你的dumb組件複用於別的state下,並且這兩個state還徹底不一樣

  • 本質上dumb 組件 其實 就是你的app的調色版。。你能夠將它們放到一個頁面上。。而後讓設計師除了app的業務邏輯,樣式隨便怎麼改,

參看文章:Smart and Dumb Components 

高階組件(HOC-higher order component) 

高階組件(HOC)是react中對組件邏輯進行重用的高級技術。但高階組件自己並非React API。它只是一種模式,這種模式是由react自身的組合性質必然產生的。

具體而言,高階組件就是一個函數,且該函數接受一個組件做爲參數,並返回一個新的組件

const EnhancedComponent = higherOrderComponent(WrappedComponent);

對比組件將props屬性轉變成UI,高階組件則是將一個組件轉換成另外一個新組件。

好處:使用高階組件(HOC)解決交叉問題

參看文章:高階組件

總結下,看看一我的的組件化水準,

  • pure component

  • functional component

  • smart, dumb component 

  • higher order component

  • hoc render hijacking

  • 會用 props.children React.children cloneElement

  • 提供 instance method

  • context

並理解react 內部實現原理

  • 懂 setState  是異步的

  • 懂 synthetic event

  • 懂 react-dom 分層和 react 沒有關係

  • 懂 reconciler

  • 懂 fiber  

具體問題以下:

  • 1. 怎麼抽象一個帶搜索,單多選複合,有請求的 Selector,區分 smart 和 dumped。若是我再往上加功能,好比 autocomplete  等

  • 2. 怎麼實現對錶單的抽象,數據驗證怎麼統一處理

  • 3. 用 react 來實現一個可視化編輯器的引擎,怎麼設計,怎麼抽象與 model 的交互,再引入 redux 呢,怎麼支持第三方組件熱插拔

  • 4. 用 react 和 redux 模擬多人協做的 Todo,node 做爲後端,怎麼設計

 

同構、純粹的javascrip

由於搜索引擎的爬蟲程序依賴的是服務端響應而不是JavaScript的執行,預渲染你的應用有助於搜索引擎優化。

 

react一些常見問題:

setState()函數在任何狀況下都會致使組件重渲染嗎?若是setState()中參數仍是原來沒有發生任何變化的state呢?

對setState用得深了,就容易犯錯,因此咱們開門見山先把理解setState的關鍵點列出來。

  • setState不會馬上改變React組件中state的值;

  • setState經過引起一次組件的更新過程來引起從新繪製;

  • 屢次setState函數調用產生的效果會合並

  • setState後,知道reader時,才真正改變state的值

    shouldComponentUpdate函數返回false,由於更新被中斷,因此不調用render,可是React不會放棄掉對this.state的更新的,依然會更新this.state

 

傳入 setState 函數的第二個參數的做用是什麼?

該函數會在setState函數調用完成而且組件開始重渲染的時候被調用,咱們能夠用該函數來監聽渲染是否完成(通常沒有什麼卵用)

 調用 setState 以後發生了什麼?

 在代碼中調用setState函數以後,React 會將傳入的參數對象與組件當前的狀態合併,而後觸發所謂的調和過程(Reconciliation)。通過調和過程,React 會以相對高效的方式根據新的狀態構建 React 元素樹而且着手從新渲染整個UI界面。在 React 獲得元素樹以後,React 會自動計算出新的樹與老樹的節點差別,而後根據差別對界面進行最小化重渲染。在差別計算算法中,React 可以相對精確地知道哪些位置發生了改變以及應該如何改變,這就保證了按需更新,而不是所有從新渲染。

 

用shouldComponentUpdate作優化的意義大嗎?shouldComponentUpdate將帶來可測量和可感知的提高?

若是不能,那就別用:你可能應該避免用它。據React團隊的說,shouldComponentUpdate是一個保證性能的緊急出口,意思就是你不到萬不得已就別用它。具體參考:何時使用shouldComponentUpdate方法?

通常狀況下setState() 確立後老是觸發一次重繪,除非在 shouldComponentUpdate() 中實現了條件渲染邏輯。若是使用可變的對象,可是又不能在 shouldComponentUpdate() 中實現這種邏輯,僅在新 state 和以前的 state 存在差別的時候調用 setState() 能夠避免沒必要要的從新渲染。

 

react異步數據如ajax請求應該放在哪一個生命週期?

對於同步的狀態改變,是能夠放在componentWillMount,對於異步的,最好好放在componentDidMount。但若是此時有若干細節須要處理,好比你的組件須要渲染子組件,並且子組件取決於父組件的某個屬性,那麼在子組件的componentDidMount中進行處理會有問題:由於此時父組件中對應的屬性可能尚未完整獲取,所以就讓其在子組件的componentDidUpdate中處理。

具體參考:《react異步數據如ajax請求應該放在哪一個生命週期?

 

React 中的 keys 是什麼,爲何它們很重要?

在開發過程當中,咱們須要保證某個元素的 key 在其同級元素中具備惟一性。在 React Diff 算法中 React 會藉助元素的 Key 值來判斷該元素是新近建立的仍是被移動而來的元素,從而減小沒必要要的元素重渲染。此外,React 還須要藉助 Key 值來判斷元素與本地狀態的關聯關係,所以咱們毫不可忽視轉換函數中 Key 的重要性。

keys 是幫助 React 跟蹤哪些項目已更改、添加或從列表中刪除的屬性。

每一個keys 在兄弟元素之間是獨一無二的。咱們已經談過幾回關於一致化處理(reconciliation)的過程,並且這個一致化處理過程(reconciliation)中的一部分正在執行一個新的元素樹與最前一個的差別。keys 使處理列表時更加高效,由於 React 可使用子元素上的 keys 快速知道元素是新的仍是在比較樹時才被移動的。

並且 keys 不只使這個過程更有效率,並且沒有keys,React 不知道哪一個本地狀態對應於移動中的哪一個項目。因此當你 map 的時候,不要忽略了 keys 。

 

受控組件( controlled component )與不受控制的組件( uncontrolled component )有什麼區別?

React 的很大一部分是這樣的想法,即組件負責控制和管理本身的狀態(任何改變代用setSate處理)

那麼不受控組件呢?組件數據不所有是setState來處理,還有DOM交互,好比refs這玩意來操控真實DOM

雖然不受控制的組件一般更容易實現,由於您只需使用引用從DOM獲取值,可是一般建議您經過不受控制的組件來支持受控組件。

 

主要緣由是受控組件支持即時字段驗證,容許您有條件地禁用/啓用按鈕,強制輸入格式,而且更多的是 『the React way』。

 

描述事件在React中的處理方式

 

爲了解決跨瀏覽器兼容性問題,您的 React 中的事件處理程序將傳遞SyntheticEvent 的實例,它是 React 的瀏覽器本機事件的跨瀏覽器包裝器。

 

這些 SyntheticEvent 與您習慣的原生事件具備相同的接口,除了它們在全部瀏覽器中都兼容。有趣的是,React 實際上並無將事件附加到子節點自己。React 將使用單個事件監聽器監聽頂層的全部事件。這對於性能是有好處的,這也意味着在更新DOM時,React 不須要擔憂跟蹤事件監聽器。

 

 

 

在什麼狀況下你會優先選擇使用 Class Component 而不是 Functional Component?

在組件須要包含內部狀態或者使用到生命週期函數的時候使用 Class Component ,不然使用函數式組件。

 

簡單介紹下react的diff

計算一棵樹形結構轉換成另外一棵樹形結構的最少操做,是一個複雜且值得研究的問題。傳統 diff 算法經過循環遞歸對節點進行依次對比,效率低下,算法複雜度達到 O(n^3),其中 n 是樹中節點的總數。O(n^3) 到底有多可怕,這意味着若是要展現1000個節點,就要依次執行上十億次的比較。這種指數型的性能消耗對於前端渲染場景來講代價過高了!現今的 CPU 每秒鐘能執行大約30億條指令,即使是最高效的實現,也不可能在一秒內計算出差別狀況。。React 經過制定大膽的策略,將 O(n^3) 複雜度的問題轉換成 O(n) 複雜度的問題。

 react的diff 策略:

  •  Web UI 中 DOM 節點跨層級的移動操做特別少,能夠忽略不計。

  •  擁有相同類的兩個組件將會生成類似的樹形結構,擁有不一樣類的兩個組件將會生成不一樣的樹形結構。 

  • 對於同一層級的一組子節點,它們能夠經過惟一 id 進行區分。 

基於以上三個前提策略,React 分別對 tree diff、component diff 以及 element diff 進行算法優化,事實也證實這三個前提策略是合理且準確的,它保證了總體界面構建的性能。 

 

  • tree diff:

    基於策略一,React 對樹的算法進行了簡潔明瞭的優化,即對樹進行分層比較,兩棵樹只會對同一層次的節點進行比較。

既然 DOM 節點跨層級的移動操做少到能夠忽略不計,針對這一現象,React 經過 updateDepth 對 Virtual DOM 樹進行層級控制,只會對相同顏色方框內的 DOM 節點進行比較,即同一個父節點下的全部子節點。當發現節點已經不存在,則該節點及其子節點會被徹底刪除掉,不會用於進一步的比較。這樣只須要對樹進行一次遍歷,便能完成整個 DOM 樹的比較。

0c08dbb6b1e0745780de4d208ad51d34_hd.jpg

updateChildren: function(nextNestedChildrenElements, transaction, context) {
  updateDepth++;
  var errorThrown = true;
  try {
    this._updateChildren(nextNestedChildrenElements, transaction, context);
    errorThrown = false;
  } finally {
    updateDepth--;
    if (!updateDepth) {
      if (errorThrown) {
        clearQueue();
      } else {
        processQueue();
      }
    }
  }
}

分析至此,大部分人可能都存在這樣的疑問:若是出現了 DOM 節點跨層級的移動操做,React diff 會有怎樣的表現呢?是的,對此我也好奇不已,不如試驗一番。

 

以下圖,A 節點(包括其子節點)整個被移動到 D 節點下,因爲 React 只會簡單的考慮同層級節點的位置變換,而對於不一樣層級的節點,只有建立和刪除操做。當根節點發現子節點中 A 消失了,就會直接銷燬 A;當 D 發現多了一個子節點 A,則會建立新的 A(包括子節點)做爲其子節點。此時,React diff 的執行狀況:create A -> create B -> create C -> delete A。

d712a73769688afe1ef1a055391d99ed_hd (1).jpg

由此可發現,當出現節點跨層級移動時,並不會出現想象中的移動操做,而是以 A 爲根節點的樹被整個從新建立,這是一種影響 React 性能的操做,所以 React 官方建議不要進行 DOM 節點跨層級的操做。

 

提示:在開發組件時,保持穩定的 DOM 結構會有助於性能的提高。例如,能夠經過 CSS 隱藏或顯示節點,而不是真的移除或添加 DOM 節點。

component diff:

  • 若是是同一類型的組件,按照原策略繼續比較 virtual DOM tree。

  • 若是不是,則將該組件判斷爲 dirty component,從而替換整個組件下的全部子節點。對於同一類型的組件,有可能其 Virtual DOM 沒有任何變化,若是可以確切的知道這點那能夠節省大量的 diff 運算時間,所以 React 容許用戶shouldComponentUpdate() 來判斷該組件是否須要進行 diff。

以下圖,當 component D 改變爲 component G 時,即便這兩個 component 結構類似,一旦 React 判斷 D 和 G 是不一樣類型的組件,就不會比較兩者的結構,而是直接刪除 component D,從新建立 component G 以及其子節點。雖然當兩個 component 是不一樣類型但結構類似時,React diff 會影響性能,但正如 React 官方博客所言:不一樣類型的 component 是不多存在類似 DOM tree 的機會,所以這種極端因素很難在實現開發過程當中形成重大影響的。

52654992aba15fc90e2dac8b2387d0c4_hd.jpg

element diff:

當節點處於同一層級時,React diff 提供了三種節點操做,分別爲:INSERT_MARKUP(插入)、MOVE_EXISTING(移動)和 REMOVE_NODE(刪除)

  • INSERT_MARKUP,新的 component 類型不在老集合裏, 便是全新的節點,須要對新節點執行插入操做。

  • MOVE_EXISTING,在老集合有新 component 類型,且 element 是可更新的類型,generateComponentChildren 已調用 receiveComponent,這種狀況下 prevChild=nextChild,就須要作移動操做,能夠複用之前的 DOM 節點。

  • REMOVE_NODE,老 component 類型,在新集合裏也有,但對應的 element 不一樣則不能直接複用和更新,須要執行刪除操做,或者老 component 不在新集合裏的,也須要執行刪除操做。

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

7541670c089b84c59b84e9438e92a8e9_hd.jpg

React 提出優化策略:容許開發者對同一層級的同組子節點,添加惟一 key 進行區分,雖然只是小小的改動,性能上卻發生了翻天覆地的變化!

7b9beae0cf0a5bc8c2e82d00c43d1c90_hd.jpg

總結

  • React 經過制定大膽的 diff 策略,將 O(n3) 複雜度的問題轉換成 O(n) 複雜度的問題;

  • React 經過分層求異的策略,對 tree diff 進行算法優化;

  • React 經過相同類生成類似樹形結構,不一樣類生成不一樣樹形結構的策略,對 component diff 進行算法優化;

  • React 經過設置惟一 key的策略,對 element diff 進行算法優化;

  • 建議,在開發組件時,保持穩定的 DOM 結構會有助於性能的提高;

  • 建議,在開發過程當中,儘可能減小相似將最後一個節點移動到列表首部的操做,當節點數量過大或更新操做過於頻繁時,在必定程度上會影響 React 的渲染性能。

 

diff算法做爲react的核心,非三言兩語可以提及道明,建議參看:React 源碼剖析系列 - 難以想象的 react diff 

 

怎麼看待不可變數據?

這個暫待完善

 

ssr (server side render)會有什麼性能問題,哪些會引發內存泄露,引入 redux 後怎麼處理請求的邏輯

 

參考:從零開始搭建React同構應用(三):配置SSR

 

參考文章:

何時使用shouldComponentUpdate方法?

setState爲何不會同步更新組件狀態

setState:這個API設計到底怎麼樣

高階組件

轉載請註明文章來源:重談react優點--react技術棧回顧 - ECMAScript,js,javascript - 周陸軍的我的網站https://www.zhoulujun.cn/html/webfront/ECMAScript/jsBase/2018_0424_8101.html,若有不妥之處,望告知!

相關文章
相關標籤/搜索