譯文《容器元件和展示元件》原作者:Dan Abramov

weixin_34107955發表於2016-02-10

前言

最初學習react時,只是認識了它的api與十分好用的jsx語法。現在嘗試著用react和meteor寫實際webapp時,就不知不覺地習慣了一些react方法。今晚偶然逛到redux作者——Dan Abramov的medium主頁,發現他的《Presentational and Container Components》文章中總結了一種模式。看後,獲益匪淺,便將其翻譯了,分享給大家。

譯文

容器元件和展示元件

容器元件和展示元件名詞都來自於redux中文文件。

我在用react寫程式時,發現了一種簡單好用的模式。如果你也熟悉react,或許它早就被你發現了。有一篇文章講得很好,但是,我想補充幾點。

如果你將元件分成兩類,你會發現它們容易更被重用和理解。這兩類我稱之為容器元件和展示元件。我也聽過其他說法,比如"臃腫的"和"苗條的","智慧的"和"單調的","多狀態變數"和"單純的","封裝物"和"元件"等等。概念不完全一致,卻有一樣的中心思想。

我所說的展示元件:

  • 只關心它們的樣子。
  • 可能同時包含子級容器元件和展示元件,一般含DOM標籤和自定的樣式。
  • 通常用<code>this.props.children</code>來包含其他元件
  • 不依賴app其它元件,比如flux的actions和stores
  • 不會定義資料如何讀取,如何改變
  • 只通過<code>this.props</code>接受資料和回撥函式
  • 很少有自己的狀態變數,即使有,也是UI的狀態變數,比如<code>toggleMenuOpen</code>,<code>InputFocus</code>
  • 一般是函式級元件,除非它們需要狀態,lifecycle hooks,優化處理。

lifecycle hook這個詞語很形象,但我不知道怎麼翻譯得貼切-_-!大概指那些監控元件生命週期裡一些關鍵時刻的函式,比如,我需要在這元件初始化的時候呼叫某函式,那麼我實現一個<code>onInit()</code>介面。react中的componentWillMount之類的函式也許也是lifecycle hook?

  • 例子有Page,Sidebar,Story,UserInfo,List

我所說的容器元件

  • 只關心它們的運作方式。
  • 可能同時包含子級容器元件和展示元件,但大都不含DOM標籤,而含他們自己所用的wrapping div,從不用自己的樣式。
  • 為展示元件或其他元件提供資料和方法。
  • 呼叫Flux的actions,並且將其作為展示元件的回撥函式。
  • 維持許多狀態變數,通常充當一個資料來源。
  • 通常由高階元件生成,比如Redux裡的connect(),Relay裡的createContainer(),Flux Utils裡的Container.create(),而非手工寫出(譯者:可能在meteor中資料是例外吧)
  • 例子有UserPage, FollowersSidebar, StoryContainer, FollowedUserList

我把他們放在不同的資料夾中,以示區別。

這種方法的好處

  • 分離關注,你可以更好的理解app和UI。
  • 更易複用,同樣的展示元件可以在不同的狀態源、資料來源中使用。也可以封裝成容器元件,在未來重用它們。
  • 展示元件是app的調色盤。你可以把它們放到單獨的頁面,並讓設計師來調整它們的樣式和結構,而不用改變app的邏輯。單獨的頁面有靜態性,你可以在上面進行screenshot regression測試。
  • 這種方法會強迫你去解析佈局相關的元件,比如Sidebar, Page, ContextMenu,強迫你去使用this.props.children,而非在不同容器中不斷複製jsx那塊地方。

記住,react的元件不一定要生成DOM,它們只需要考慮如何設計UI之間的分界與組合關係。利用好這一點。

什麼時候引入容器?

我的建議是,你最好先做展示元件。當你意識到,有一些中間元件傳遞了過多的props,有一些元件並不使用它們繼承的props而只是將這些props傳遞給他們的子級,而且每次子級元件需要更多資料時,你都需要重新調整或編寫這些中間元件,那麼,這時候你可以考慮引入容器元件了。這樣做,你可以傳遞props和方法給末端的子級元件,而不必麻煩一些不相關的中間元件。
這是一個邊寫邊改的重構,所以不必一步到位。你嘗試著這種模式,慢慢地會培養起一種對何時引入容器的直覺,就像你知道何時該增加函式一樣。我的redux教程也許會幫助你哦

其他二分法

容器元件和展示元件的分別並不被嚴格定義,理解這一點很重要。為了對比,我再列舉一些相關(但是不同的)的二分法。

多狀態變數和少狀態變數

有些元件用<code>setState()</code>,有些不用。容器元件通常多狀態變數,而展示元件卻不,這不是鐵規律。展示元件也可以多狀態變數,而容器元件也可以少狀態變數。

類與函式

自從React0.14,元件可以被宣告為類,也可以被宣告為函式。定義函式方便,卻少了類獨有的特性。有些限制或許會在未來消失,但是它們至少是存在的。因為函式容易理解,所以我推薦使用函式,除非你需要那些,現在只有類才有的狀態管理,lifecycle hooks,效能優化等特性。

純的不純的

人們說一個元件純,是指給予它一樣props和state,它保證能輸出一樣的結果。純函式可以是類,可以是函式,可以多狀態,可以無狀態。Another important aspect of pure components is that they don’t rely on deep mutations in props or state, so their rendering performance can be optimized by a shallow comparison in their shouldComponentUpdate() hook。目前只有類可以定義<code>shouldComponentUpdate()</code>,或許將來有改變吧。

展示元件和容器元件都有上述的二分特性。在我看來,展示元件傾向於少狀態、純的函式,容器元件傾向於多狀態,純的類。當然啦,這只是個人觀察,而非規則,我也見過完全相反的情況。
不要將展示元件和容器元件當作教條。有些時候,不必劃出清晰的線條,也不用覺得劃出區分會很困難。如果你分不清某個元件是展示元件還是容器元件,也許是為時尚早。要知道,心急吃不了熱豆腐。

例子

This gist by Michael Chan 討論了這個

延伸閱讀

Getting Started with Redux
Mixins are Dead, Long Live Composition
Container Components
Atomic Web Design
Building the Facebook News Feed with Relay

相關文章