對於封裝react組件的一些思考

因爲近期在涉及到封裝組件的時候遇到了一些問題,因而我認真地瞭解了一下react封裝組件過程當中應該要涉及和思考到的一些問題,寫了下來。(如下主要是針對UI組件,因爲水平有限不保證內容正確性,僅僅是一些我的的思考)css

1、什麼是組件

組件能夠將UI切分紅一些的獨立的、可複用的部件,這樣就只需專一於構建每個單獨的部件。node

所謂組件,即封裝起來的具備獨立功能的UI部件。react

在 React 中,一切皆是組件,所以理解組件的工做流與核心尤其重要。es6

且react中有多種建立組件的方式和各類各樣的組件概念,所以在設計組件的時候應該使用哪一種組件的建立方式且應該設計一個怎樣的組件都值得深刻思考。數組

那麼在React裏面一個組件應該有什麼特徵呢?在react中認爲組件應該具備以下特徵:

  1. 可組合(Composeable):一個組件易於和其它組件一塊兒使用,或者嵌套在另外一個組件內部。若是一個組件內部建立了另外一個組件,那麼說父組件擁有(own)它建立的子組件,經過這個特性,一個複雜的UI能夠拆分紅多個簡單的UI組件;
  2. 可重用(Reusable):每一個組件都是具備獨立功能的,它能夠被使用在多個UI場景;
  3. 可維護(Maintainable):每一個小的組件僅僅包含自身的邏輯,更容易被理解和維護;

2、一個設計良好的組件應該有什麼特性?

(一)高內聚、低耦合

咱們常常談一個設計良好的系統應該是高內聚低耦合的,那麼其實我認爲一個好的組件也應該是具備高內聚低耦合的特性。bash

那麼咱們應該要怎麼去作到使一個組件實現高內聚低耦合的特色呢?

  1. 高內聚:將邏輯緊密相關的內容放在一個組件內。
    React能夠將展現內容的JSX、定義行爲的JavaScript代碼、甚至定義樣式的css,
    均可以放在一個JavaScript文件中,所以React天生具備高內聚的特色。
  2. 低耦合:不一樣組件之間的依賴關係要儘可能弱化。
    也就是每一個組件要儘可能獨立,
    一個組件不該該掌握着其餘組件的細節,
    而是要儘可能作到對其餘組件瞭解不多,甚至是一無所知。

爲何須要實現低耦合呢?

由於低耦合會帶來如下的好處:異步

  1. 在系統中的局部改變不會影響到其餘地方
  2. 任何組件均可以被替代品取代
  3. 系統之間的組件能夠複用
  4. 能夠輕易測試獨立的組件,提升了應用的測試代碼覆蓋率

而高耦合的組件間會很容易出現一個問題,
就是沒法或者很艱難去修改一個大量依賴其餘組件的組件,
甚至只是改一個用來傳遞數據的字段都會致使大量的修改。函數

(二)隱藏內部結構

一個封裝良好的組件應該是要隱藏其內部結構的,並經過一組 props來提供控制其行爲的途徑。性能

隱藏內部結構是必須的。內部結構或實現細節不該該能被其餘組件知道或關聯。測試

React 組件能夠是函數式的,也能夠是基於類的,能夠定義實例方法、設置 refs、維護 state或是使用生命週期方法。而這些實現細節被封裝在組件自身中,其餘組件不該該窺見其中的任何細節。

基於此特色來設計的組件對其餘組件的依賴是極低的,帶來的是低耦合的特色和好處。

(三)職責單一

我認爲組件應該要符合單一職責原則,
一個組件應該儘可能只負責一件事情,而且把這件事情作好,
由於我以爲一個組件若是負責處理的事情過多,
在修改其中一件事情的時候頗有可能也會影響到它負責的其餘事情,且不利於維護和複用。

3、在封裝一個組件的時候應該先思考什麼?

  1. 這個組件應該是作什麼的
  2. 這個組件應該至少須要知道那些信息
  3. 這個組件會反饋什麼東西

在設計一個組件的時候咱們不該該僅限於實現當前的需求,
設計出一個只適用於單一項目的組件,而是應該是一個能夠適應大部分同種需求的通用組件。
因此咱們在碰到一個需求的時候應該首先對需求進行抽象,而不是看到設計稿就擼着袖子上。

例如碰到一個輪播圖組件的需求的時候,咱們拆分如下這個需求,能夠獲得:

(1) 這個組件要作什麼:

  1. 能夠展現多張圖片
  2. 能夠向左向右翻頁,或者是能夠是上下翻頁
  3. PageControl的狀態會根據圖片的滾動而相應改變 還有可能有一些隱藏的需求,相似於:
  4. 應該支持左右兩側或者上下無限循環滾動
  5. 能夠選擇的是否自動輪播
  6. 支持手動滑動切換圖片
  7. 圖片有點擊事件,能夠點擊來進行相關的事件反應

(2)這個組件至少應該知道什麼信息

一個好的組件應該是要像存在魔法同樣,只須要極其少數的參數和條件就能夠獲得指望的效果。就像這個輪播圖組件同樣,組件應該至少知道的信息有:

  1. 圖片的url地址數組
  2. 當圖片不存在時候的佔位圖

其餘能夠知道也能夠不知道的信息能夠有:

  1. 是否開啓自動輪播,默認是開啓或者不開啓

  2. 圖片滾動是左右仍是上下,默認是左右

    等等 ....................................

(3)這個組件會反饋什麼

一個可用的輪播圖效果

4、組件的通訊

父組件向封裝好的子組件通訊一般是經過props

做爲組件的輸入,props的值應該最好是js基本類型 (如 string、number、boolean)
可是props能夠傳入的不只僅只是這些,它但是一個神奇的東西,它能夠傳入包括:

  1. js基本類型(如 string、number、boolean)
<Message text="Hello world!" modal={false} />;  
複製代碼
  1. 對象
<Message
  data={{ 
  thexAxis:  thexAxis ,     
  lineData : lineData
   }} 
  />
複製代碼
  1. 數組
<MoviesList items={['Batman Begins', 'Blade Runner']} />  
複製代碼
  1. 做爲事件處理和異步操做時,能夠指定爲函數:
<Message type="text" onChange={handleChange} />  
複製代碼
  1. prop 甚至能夠是一個組件構造器。組件可被用來處理其餘組件的實例化:
function If({ Component, condition }) {  
 return condition ? <Component /> : null;
  }
<If condition={false} component={LazyComponent} />  
複製代碼

爲避免破壞封裝,要謹慎對待 props 傳遞的細節。
父組件對子組件設置 props 時,也不該該暴露自身的結構。
好比,把整個組件實例或 refs 當成 props 傳遞之類的神奇操做。

訪問全局變量是另外一個對封裝形成負面影響的問題。

咱們能夠經過 proptypes來對傳入的數據進行類型限制。

5、react中建立組件的方法

react建立組件有三種方法,分別是:

  1. function式無狀態組件
  2. es5方式React.createClass組件
  3. es6方式extends React.Component

而目前react推薦ES5方式和ES6方式建立組件的寫法中推薦的是ES6的寫法,因此這裏就不對ES5的寫法進行討論了。

React.Component

React.Component是以ES6的形式來建立React組件,也是如今React官方推薦的建立組件的方式,
其和React.createClass建立的組件同樣,也是建立有狀態的組件。

相比React.createClass方式,React.Component帶來了諸多語法上的改進

1.import

ES6使用import方式替代ES5的require方式來導入模塊,其中import { }能夠直接從模塊中導入變量名,此種寫法更加簡潔直觀。

2.初始化 state

在ES6的語法規則中,React的組件使用的類繼承的方式來實現,去掉了ES5的getInitialState的hook函數,state的初始化則放在constructor構造函數中聲明。

引伸內容:

如何正肯定義State

React把組件當作一個狀態機。經過與用戶的交互,實現不一樣狀態,而後渲染UI,讓用戶界面和數據保持一致。 組件的任何UI改變,均可以從State的變化中反映出來; State中的全部狀態都用於反映UI的變化,不該有多餘狀態。

那麼什麼樣的變量應該作爲組件的State呢:

  1. 能夠經過props從父組件中獲取的變量不該該作爲組件State。
  2. 這個變量若是在組件的整個生命週期中都保持不變就不該該做爲組件State。
  3. 經過其餘狀態(State)或者屬性(Props)計算獲得的變量不該該做爲組件State。
  4. 沒有在組件的render方法中使用的變量不用於UI的渲染,那麼這個變量不該該做爲組件的State。這種狀況下,這個變量更適合定義爲組件的一個普通屬性。

function和class建立組件的區別

React內部是經過調用組件的定義來獲取被渲染的節點,而對於不一樣的組件定義方式,其獲取節點的步驟也不同。以下:

//function方式定義
function Example() {
  return <div>this is a div</div>;
}

const node = Example(props);

// 類方式定義
class Example extends React.Component {
  render() {
    return <div>this is a div</div>;
  }
}

const instance = new Example(props);
const node = instance.render();
複製代碼

在這裏,函數直接調用,類則須要先實例化再去調用實例化對象上的render方法;

若是將類按照普通函數去調用則會報錯

6、Component 和 PureComponent

由於這方面沒有詳細去了解過,因此也只是粗淺總結一下其區別:

PureComponent除了提供了一個具備淺比較的shouldComponentUpdate方法,
PureComponent和Component基本上徹底相同。
當組件更新時,若是組件的 props 和 state 都沒發生改變, render 方法就不會觸發,省去 Virtual DOM 的生成和比對過程,達到提高性能的目的。

若是咱們想用PureComponent去代替Component的時候不須要去作太多的事情,
僅僅是把Component改爲PureComponent便可。 可是咱們並不是能夠在全部地方都用PureComponent去代替Component,
具體仍是要按照實際狀況來選擇,由於瞭解不深就不在此處詳談了。

7、有狀態組件和無狀態組件

無狀態組件更多的是用來定義模板,接收來自父組件props傳遞過來的數據,
使用{props.xxx}的表達式把props塞到模板裏面。
無狀態組件應該保持模板的純粹性,以便於組件複用,因此一般UI組件應該是無狀態組件。
相似於:

var Header = (props) = (
        <div>{props.xxx}</div>
   );
複製代碼

而有狀態組件一般是用來處理定義交互邏輯和業務數據
(使用{this.state.xxx}的表達式把業務數據掛載到容器組件的實例上(有狀態組件也能夠叫作容器組件,無狀態組件也能夠叫作展現組件),
而後傳遞props到展現組件,展現組件接收到props,把props塞到模板裏面。
相似於:

class Home extends React.Component {
  constructor(props) {
      super(props);
      };
   render() {
      return (
         <Header/> 
      )
   }
}
複製代碼

8、高階組件

高階組件給個人感受相似於高階函數,都是接受一個東西的輸入, 而後再給輸入的東西添加新的特性做爲一個新的東西輸出, 看起來相似於裝飾器模式的實現。 可是由於目前爲止沒有寫太高階組件,因此就不在這裏討論了。

相關文章
相關標籤/搜索