1. 程式人生 > >用好React,你必須要知道的事情

用好React,你必須要知道的事情

容器性元件(container component)和展示性元件(presentational component)

使用React編寫元件時,我們需要有意識地將元件劃分為容器性元件(container component)和展示性元件(presentational component),這樣有助於我們在編寫元件時,更加明確這個元件應該負責哪些事情。

容器性元件,負責業務流程邏輯的處理,如傳送網路請求,處理請求資料,將處理過的資料傳遞給子元件的Props使用。同時,容器性元件提供源資料的方法,以Props方式傳遞給子元件,當子元件的狀態變更引起源資料的變化時,子元件通過呼叫容器性元件提供的方法同步這些變化。

展示性元件,負責元件的外表,也就是元件如何渲染,具有很強的內聚性。展示性元件不關心渲染時使用的元件屬性(Props)是如何獲取到的,它只要知道有了這些Props後,元件應該如何渲染就足夠了。屬性如何獲取,是容器性元件負責的事情。當展示性元件狀態的變化需要同步到源資料時,需要呼叫容器性元件中的方法,這個方法一般也是通過Props傳遞給展示性元件。

例如,一個Todo專案,有一個Todo元件和一個TodoList元件,Todo元件是一個容器性元件,負責從伺服器端獲取待辦事項列表,獲取到待辦事項列表後傳遞給TodoList顯示。當在TodoList中新建一項待辦事項後,需要通過TodoList 的 Props,呼叫Todo元件中儲存待辦專案的方法,將新建的待辦專案同步到伺服器端。

容器性元件和展示性元件可以相互巢狀,一個容器性元件可以包含多個展示性元件和其他的容器性元件;一個展示性組將也可以包含容器性元件和其他的展示性元件。這樣的分工,可以使與元件渲染無直接關係的邏輯由容器性元件集中負責,展示性元件只關注元件的渲染邏輯,從而使展示性元件更容易被複用。對於非常簡單的頁面,一般只要一個容器性元件就足夠了;但對於負責頁面,則需要多個容器性元件,否則所有的業務邏輯都在一個容器性元件中處理的話,會導致這個元件非常複雜,同時這個元件獲取到的源資料,可能需要經過很多層的元件Props的傳遞,才能到達最終使用的展示性元件。

Props、State和元件的普通屬性

Props、State的概念都很清晰,元件的普通屬性是指在元件中直接掛載到this下的屬性。其實,Props和State也是元件的兩個普通屬性,因為我們可以通過this.props 和 this.state 直接獲取到。那麼Props、State 和 元件的其他普通屬性,分別應該在什麼場景下使用呢?

Props和State都是用於元件渲染的,也就是說,一個元件最終長成什麼樣,取決於這個元件的Props和State。Props和State的變化都會觸發元件的render方法。但這兩者也是有區別的。Props是隻讀的資料,它是由父元件傳遞過來的;而State是元件內部自己維護的狀態,是可變的。State可以根據Props的變化而變化。如果元件中還需要其他屬性,而這個屬性又與元件的渲染無關(也就是render方法中不會用到),那麼就可以把這個屬性直接掛在到this下,而不是作為元件的一個狀態。

例如,元件中需要一個定時器,每隔幾秒改變一下元件的狀態,就可以定義一個this.timer屬性,以備在componentWillUnmount時,清除定時器。

setState 非同步性

React官網提到,this.state和this.props的更新可能是非同步的,React可能會出於效能考慮,將多個setState的呼叫,合併到一次State的更新中。所以,不要依賴this.props 和 this.state的值計算下一個狀態。引用官網的一個程式碼示例:

// Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});

如果一定要這麼做,可以使用另一個以函式作為引數的setState方法,這個函式的第一個引數是前一個State,第二個引數是當前接收到的最新Props。如下所示:

// Correct
this.setState(function(prevState, props) {
  return {
    counter: prevState.counter + props.increment
  };
});

在呼叫setState之後,也不能立即使用this.state獲取最新狀態,因為這時的state很可能還沒有被更新,要想保證獲取到的state是最新的state,可以在componentDidUpdate中獲取this.state。也可以使用帶用回撥函式引數版本的setStatesetState(stateChange, [callback]),回撥函式中的this.state會保證是最新的state。

componentWillReceiveProps

當元件的屬性可能發生變化時,這個方法會被呼叫。這裡說可能,是因為父元件render方法每次被呼叫時,子元件的這個方法都會被呼叫(子元件第一次初始化時除外),但並不一定每次子元件的屬性都會發生變化。如果元件的State需要根據Props的變化而變化,那麼這個方法就是最適合這個這個邏輯的地方。例如當Props變化時,元件的State需要重置,就可以在這個方法中呼叫this.setState()來重置狀態。需要注意,在這個方法中呼叫this.setState()並不會重新觸發componentWillReceiveProps的呼叫,也不會導致render方法被觸發兩次。一般情況下,接收到新Props會觸發一次render,呼叫this.setState也會觸發一次render,但在componentWillReceiveProps中呼叫this.setState,React會把原本需要的兩次render,合併成一次。

shouldComponentUpdate

這個方法常作為優化React效能使用。當shouldComponentUpdate返回false時,元件本次的render方法不會被觸發。可以通過在這個方法中比較前後兩次state或者props,根據實際業務場景決定是否需要觸發render方法。

React提供了一個React.PureComponent元件,這個元件重寫了shouldComponentUpdate,會對前後兩次的state和props進行淺比較,如何不一致,才會返回true,觸發後續的render方法。這裡的淺比較指,只會對state和props的第一級屬性進行比較(使用!==),這滿足一般的使用場景。如果你的元件繼承了React.PureComponent,但在setState時,傳入的state是直接修改的原有state物件,就會因為依然滿足淺比較的條件,而不會重新觸發render方法,導致最終DOM和state不一致。例如state={books: ['A','B']},在setState時,使用this.setState({name: this.state.books.push('C')})直接修改books物件,這樣雖然books內容發生了修改,但因為物件引用並沒有變化,所以依然滿足淺比較條件,不會觸發render方法。

一般情況下,讓shouldComponentUpdate返回預設的true是不會有太大問題的。雖然這樣可能導致一些不必要的render方法被呼叫,但render方法直接操作的是虛擬DOM,只要虛擬DOM沒有發生變化,並不會導致實體DOM的修改。而JS慢是慢在實體DOM的修改上。只要你的render方法不是很複雜,多呼叫幾次render方法並不會帶來多大的效能開銷。

render

父元件每次render方法被呼叫,或者元件自己每次呼叫setState方法,都會觸發元件的render方法(前提是shouldComponentUpdate使用預設行為,總是返回true)。那麼元件每次render,是不是都會導致實體DOM的重新建立呢?答案是,不是!

React之所以比直接操作DOM的JS庫快,原因是React在實體DOM之上,抽象出一層虛擬DOM,render方法執行後,得到的是虛擬DOM,React 會把組將當前的虛擬DOM結構和前一次的虛擬DOM結構做比較,只有存在差異性,React才會把差異的內容同步到實體DOM上。如果兩次render後的虛擬DOM結構保持一致,並不會觸發實體DOM的修改。

React速度快的原因,還有一個是它出色的Diff演算法。標準的比較兩棵樹的Diff演算法的時間複雜是 O(n3) 。而React基於非常符合實際場景的兩個假設,就將Diff演算法的時間複雜度降到了接近O(n)。這兩個假設是:

  1. 如果兩個元件或元素型別不同,那麼他們就是完全不同的樹,不需要再比較他們的子節點。例如,<Article><Comment>將產生是兩個完全的樹狀結構;<div>children</div><p>children</p>也是兩個完全不同的樹。這種情況下,元件會被完全重建,舊的DOM節點被銷燬,元件經歷componentWillUnmount(),然後重新建立一棵新樹, 元件經歷 componentWillMount()componentDidMount()

  2. 可以為元件或元素設定key屬性,key用來標識這個元件或元素。key不需要全域性唯一,只需要在兄弟元件或兄弟元素間保證唯一性就可以。key常用到集合(List)元素中。例如:

<ul>
<li key='a'>Book A</li>
<li key='b'>Book B</li>
</ul>

當在第一個位置插入一條記錄Book C 時,

<ul>
<li key='c'>Book C</li>
<li key='a'>Book A</li>
<li key='b'>Book B</li>
</ul>

由於有key的標識,React知道此時新增了一條記錄,會建立一個新的<li>元素,並把它插入到列表中的第一個位置。如果沒有設定key,React並不知道是新增了一條記錄,還是原來的兩條記錄完全替換成新的三條記錄,或者其他更加複雜的修改場景。React需要自上而下的比較每一條記錄,這樣每次比較節點都不同,所以需要修改兩次節點,然後再新增一個節點,效率明顯要差很多。

這裡同時揭露了另一個問題,不要使用元素在集合中的索引值作為key,因為一旦集合中元素順序發生改變,就可能導致大量的key失效,進而引起大量的修改操作。

如何傳送網路請求

當我們需要從伺服器獲取資料時,我們應該在元件的哪一個生命週期方法中傳送網路請求呢?React官網上提到,可以在componentDidMount中傳送網路請求,這也是一般情況下的最佳實踐。有些人也會把傳送網路請求放在componentWillMount中,並且認為這個方法先於componentDidMount呼叫,所以可以更快地獲取資料。個人認為,這種使用方法一般也是沒有問題的,但在一些場景下會出現問題,比如需要在伺服器端渲染時,componentWillMount會被呼叫兩次,一次是在Server端,一次是在Client端。可參考這篇文章

相關推薦

React必須知道事情

容器性元件(container component)和展示性元件(presentational component) 使用React編寫元件時,我們需要有意識地將元件劃分為容器性元件(container component)和展示性元件(presentatio

web前端之響應式佈局必須知道

一、前言 響應式Web設計可以讓一個網站同時適配多種裝置和多個螢幕,可以讓網站的佈局和功能隨使用者的使用環境(螢幕大小、輸入方式、裝置/瀏覽器能力)而變化。本文主要介紹一些響應式佈局容易忽略但又很重要的知識點。 二、視口 移動前端中常說的 viewport (視口)就是瀏覽器中用於呈現網

關於Http協議必須知道

轉自:https://segmentfault.com/a/1190000016751071      引言 HTTP協議是Hyper Text Transfer Protocol(超文字傳輸協議)的縮寫,是用於從全球資訊網伺服器傳輸超文字到本地瀏覽器的傳送協議。HT

關於響應式布局必須知道

不可 ride 元素 移動端 打印 告訴 自己的 適配 com 轉載自奇舞周刊 前言 響應式Web設計可以讓一個網站同時適配多種設備和多個屏幕,可以讓網站的布局和功能隨用戶的使用環境(屏幕大小、輸入方式、設備/瀏覽器能力)而變化。本文主要介紹一些響應式布局容易忽略但又很

關於響應式佈局必須知道

轉載自奇舞週刊 前言 響應式Web設計可以讓一個網站同時適配多種裝置和多個螢幕,可以讓網站的佈局和功能隨使用者的使用環境(螢幕大小、輸入方式、裝置/瀏覽器能力)而變化。本文主要介紹一些響應式佈局容易忽略但又很重要的知識點。 視口 移動前端中常說的 viewport (視口)就是瀏覽器中用於呈現網頁的區

響應式佈局必須知道

一、前言 響應式Web設計可以讓一個網站同時適配多種裝置和多個螢幕,可以讓網站的佈局和功能隨使用者的使用環境(螢幕大小、輸入方式、裝置/瀏覽器能力)而變化。本文主要介紹一些響應式佈局容易忽略但又很重要的知識點。 二、視口 移動前端中常說的 viewport (視

學習web前端前必須知道這些少走很多彎路

IT行業是所有行業中能力要求最高的,相應的薪水待遇也是讓人羨慕。想要勝任一份高薪的工作並不那麼容易,新手甚至0基礎的小白想要踏入這一行業需要了解些什麼呢?   在之前的幾年IT行業一度超越了掙錢最多的金融行業,因為如果說在中國所有行業中,給人打工的話,做軟體開發這塊應該是工資提升的最顯著最快的,

想做OTT-TV/IPTV網絡IP電視直播運營商-必須知道的事

OTT-TV IPTV 網絡IP電視、直播 廣播電教系統需求: – 整套系統穩定、可靠; – 支持主從設備堆疊部署,單臺設備最大支持127臺從機; – 支持RTSP/RTMP/HLS等標準的流媒體格式; – 可基於標

股票投資一定知道的運氣三定律

而在 線上 決定 中國目前 減少 自己 方向 我們 投資人 https://zhuanlan.zhihu.com/p/27190792 股票投資過程中,許多人對運氣在投資中的認識有誤區,而且想的也不是很清楚,我們今天就來討論討論這個話題。我總結了許多人對於運氣的看法,提出運

關於備份必須了解的內容

二周 增量 進行 允許 文件 例如 簡便 價值 發現 冷備份:也被稱為離線備份,是指在關閉應用並且應用不能更新的狀況下進行的數據的完整備份 手動備份:要備份到其它磁盤上(避免本地故障,數據丟失) 自動備份:首次完整備份,以後備份為增量備份(例如:一個月備份一次,第一周為完整

這個Excel技巧一定知道!旋風圖該如何制作?

順序 images proc 遇到 添加 逆序 效果圖 標簽 一個 當我們在工作中遇到分析公司人員分布、產品優劣勢等情況時,旋風圖是最常使用的一個圖表,可以讓上司一目了然地了解公司人員狀況以及產品的差異,所以學會旋風圖的制作是非常必要的,今天小編就給大家分享一下旋風圖的制作

Python 必須知道的 Flask

Flask介紹 Flask 是一個輕量級的 web 開發框架, 使用 Python 開發, 上手簡單。 安裝 Flask pip install Flask 第一個 Flask 程式 1、編寫 app.py 檔案內容如下: #encoding: utf-8 # 匯入Flas

手機怎麼錄照片上傳到抖音短視訊這個必須知道

經常玩抖音的朋友可能一直在好奇,有些人上傳的並不是連續的視訊,而是一張一張的照片,並且照片上還有相應的配文,其實原理和上傳的視訊是一樣的,需要提前將這些圖片做成一個圖集的視訊形式,然後在上傳到抖音,那這些圖集是怎樣錄製的呢? 方法/步驟: 1.首先先在手機設定中找

關於ES模組必須知道的一些禁忌(一)

背景 ES Module是JavaScript在ES2015版本開始提供的語言標準級別的模組化方案,在此之前JavaScript一直沒有語言級別的模組化體系。沒有模組化的支援,使用JavaScript開發大型應用將舉步維艱,所以經過大量的實踐,社群制定了一些模組載入方案,最主要的有運行於瀏覽器的AMD方案和

小白怎麼成為全職自媒體人?必須知道的幾點!

之前看有大部分學員都是用下班時間或者是課餘時間來操作和運營自媒體,也有學員問到是不是放棄工作來全職做自媒體就能賺到更多錢呢?(在校學生不建議放棄課業來全身心投入。) 首先我們得搞明白一個順序,就是那些全職的自媒體人究竟是先放棄工作再來鑽研操作自媒體的,還是已經鑽

Android基礎篇之Android快速入門--必須知道的基礎

1. Activity的理解: 2. Intent的理解 關於IntentFilter 3. Intent的使用:(建立、攜帶資料、讀取資料) 1.建立:      顯式意圖: Intent intent = new Inten

從C過度到C++必須掌握的知識點

內容原創,未經本人同意請勿轉載。聯絡本人:[email protected] 1,virtual函式 函式之前加上virtual關鍵字就表示該函式為虛擬函式,在派生類中通過重寫該虛擬函式來實現對基類函式的覆蓋。 //基類中定義virtua

不求重複造輪子必須知道輪子是怎麼造的。

前端的基本組成: 結構 html 表現 css 行為 JS 文件說明 宣告文件的型別,是Html4 還是 xhtml 還是H5 <!DOCTYPE html/>H5的文件說明 字符集 宣告一個網頁的編碼的格式。

Java基礎總結篇--JavaSE必須知道的基礎

一、集合:就像是一種容器。用於儲存、獲取、操作物件的容器。 1. 陣列的弊端 ①陣列的長度不可變     ②陣列沒有提供可以檢視有效元素個數的方法 2. 集合的特點 ①集合的長度是可變的 ②集合可以儲存任意型別的物件 ③集合只能儲存物件 3. 集合框架 java.util.Co

研發 | Unity資源商店裡的免費資源一定知道

這裡我們列了一個Unity資源商店必不可少的免費資源列表。 Unity 資源列表: 相機類: 1. RTScamera : Unity商店裡最好的相機外掛資源。 2. EZCamera Shake : EZ Camera Shake 系統提供了多種實現攝像頭搖晃動畫的介