我們已經學習如何編寫一個組件可以顯示資料並且能夠處理使用者的操作, 接下來, 讓我們展示 React 最棒的特色: 組合性.
動機: 劃分職責
透過建構模組化的組件能讓我們重覆利用被定義清楚的界面, 你可以使用 functions 或是 classes 來得到很多像這樣的好處. 特別是你可以透過建立新的組件時, 拆分在應用程式中不同的職責. 透過為你的應用程式建構自訂的組件庫的同時, 你將能以一種最貼切的方式表達使用者界面.
讓我們開始簡單地建立一個透過 Facebook Graph API 顯示使用者的代表圖片和名稱的大頭照組件:
Composition Example
1 | var Avatar = React.createClass({ |
Ownership
在上面這個範例中, Avatar 這個實例 (instance) 擁有 ProfilePic 和 ProfileLink 的實例. 在 React 中, 一個組件擁有者 (owner) 代表著一個可以設定 props 給其它組件的組件. 更正確地說, 如果一個組件 x 從組件 y 的 render() 方法中被建立, 就是指 x 被 y 所擁有. 簡單地說, 一個組件不能自己改變 props 的值, 都是透過它們的擁有者 (owner) 設定以保持界面的一致性. 這是 property 讓各界面能夠確保一致性的關鍵.
Children
當你建立一個 React 組件實例時, 你可以像這樣在 tag 開頭和結尾中加入額外的 React 組件或是 Javascript 表達式:
1 | <Parent><Child /></Parent> |
Parent 可以透過存取一個特別的 this.props.children prop 來讀取它的子元素.
由於 this.props.children 是一個不透明的資料結構, 你可以使用 React.Children 工具 來操作它們.
React.Children.map
1
object React.Children.map(object children, function fn [, object context])
React.Children.forEach
1
React.Children.forEach(object children, function fn [, object context])
React.Children.count
1
number React.Children.count(object children)
React.Children.only
1
object React.Children.only(object children)
Child Reconciliation
子元素的調節是一種當 React 在每一個新的 render 傳遞中更動 DOM 的處理過程
一般來說, 子元素依據它們被呈現的排列順序所調節, 舉例來說, 假設有兩個 render passes 產生這樣子的片段:
1 | // Render Pass 1 |
看起來是 <p>Paragraph 1</p> 被移除了. 但相反地, React 的 DOM 調節是將第一個元素的文字改變, 然後移除掉最後一個元素. React 的調節是依據子元素的排列順序.
Stateful Children
對大部分的組件而言這不是一個大問題, 然而, 對於有狀態的組件來說, 要維護那些在 render 階段被傳遞的 this.state 資料將會是一件很麻煩的事.
在大部分的時候, 要避免用隱藏元素代替銷毀它的這種方法:
1 | <!-- Render Pass 1 --> |
Dynamic Children
當遇到子元素經常變動的這種情形(像搜尋結果), 或是當新的組件被安插進列表中的最前面(像是 streams), 這使得界面的開發更複雜了一些. 在這種情況下, 必須給予每一個子元素一個識別 (identity) 才能在渲染過程 (render) 中維護他們的狀態. 為每一個子元素加上一個叫作 key 的 prop:
1 | render: function() { |
當 React 調節那些被標記的子元素時, 會確保被標記的所有子元素都會被記錄或銷毁.
這個唯一識別 key 應該要從某個陣列中直接帶入到組件 (component) 中, 而不是放在 HTML 子元素 (child) 裡.
1 | // WRONG! |
你也可以直接用物件的方式來標識子元素, 物件的索引將會被用來當作 key 的值. 不過在這裡有一件很重要的事需要提醒一下, Javascript 並不能確保能夠保存 properties 的排序. 在瀏覽器中除非能用 32-bit unsigned integers 才能保存 property 的排序. 數字型的 properties 會依據大小排列在其它 properties 之前. 如果發生 React render 組件的排序問題, 為 key 加上 prefix 可以解決:
1 | render: function() { |
Data Flow
在 React 中, 資料流如之前所說的透過 props 從擁有者 (owner) 傳遞到被擁有的組件. 這是有效的單向綁定: 擁有者用自己的 props 或是 state, 經由計算後產生出的某個值, 將其綁定成被擁有組件的 props. 因為這個處理會遞迴地發生, 在任何有使用資料的地方都會自動地反映改變後的結果.
A Note on Performance
你可能會想一個擁有者 (owner) 底下如果有大量的結點會造成 React 花費很高的資源來改變資料. 好消息是 Javascript 的速度相當快, 而且 render() 方法也夠簡單, 所以在大部分的應用仍能表現出色. 此外, 效能的瓶頸大部分都是來自於 DOM 的修改而不是 Javascript 的執行, React 會透過批量操作和變動偵測幫你做到最佳化的處理.
然而, 有時候你會想要有效率地且細膩的控制. 在這種情況, 可以簡單地覆寫 shouldComponentUpdate() 這個方法並在你想要 React 略過子樹 (subtree) 處理時回傳一個 false 值. 請見 React reference docs 了解更多相關的資訊.
注意:
如果當資料確實改變了但 shouldComponentUpdate() 卻回傳 false 值, React 會無法同步你的使用者界面. 請確保你了解後再使用這個方法, 並且只在當有明顯的效能問題時才去用到它. 不要因為 Javascript 比 DOM 快很多而掉以輕心.