<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          React組件應(yīng)該如何封裝?

          共 9407字,需瀏覽 19分鐘

           ·

          2021-03-11 20:54



          翻譯:劉小夕

          原文鏈接:https://dmitripavlutin.com/7-architectural-attributes-of-a-reliable-react-component/

          原文的篇幅非常長,不過內(nèi)容太過于吸引我,還是忍不住要翻譯出來。此篇文章對編寫可重用和可維護(hù)的React組件非常有幫助。但因?yàn)槠鶎?shí)在太長,我對文章進(jìn)行了分割,本篇文章重點(diǎn)闡述  封裝。因本人水平有限,文中部分翻譯可能不夠準(zhǔn)確,如果您有更好的想法,歡迎在評論區(qū)指出。

          更多文章可戳:  https://github.com/YvetteLau/Blog


          封裝

          一個封裝組件提供 props 控制其行為而不是暴露其內(nèi)部結(jié)構(gòu)。

          耦合是決定組件之間依賴程度的系統(tǒng)特性。根據(jù)組件的依賴程度,可區(qū)分兩種耦合類型:

          • 當(dāng)應(yīng)用程序組件對其他組件知之甚少或一無所知時,就會發(fā)生松耦合。

          • 當(dāng)應(yīng)用程序組件知道彼此的許多詳細(xì)信息時,就會發(fā)生緊耦合。

          松耦合是我們設(shè)計應(yīng)用結(jié)構(gòu)和組件之間關(guān)系的目標(biāo)。

          松耦合應(yīng)用(封裝組件)

          松耦合會帶來以下好處:

          • 可以在不影響應(yīng)用其它部分的情況下對某一塊進(jìn)行修改。、

          • 任何組件都可以替換為另一種實(shí)現(xiàn)

          • 在整個應(yīng)用程序中實(shí)現(xiàn)組件復(fù)用,從而避免重復(fù)代碼

          • 獨(dú)立組件更容易測試,增加了測試覆蓋率

          相反,緊耦合的系統(tǒng)會失去上面描述的好處。主要缺點(diǎn)是很難修改高度依賴于其他組件的組件。即使是一處修改,也可能導(dǎo)致一系列的依賴組件需要修改。

          緊耦合應(yīng)用(組件無封裝)

          封裝信息隱藏 是如何設(shè)計組件的基本原則,也是松耦合的關(guān)鍵。

          信息隱藏

          封裝良好的組件隱藏其內(nèi)部結(jié)構(gòu),并提供一組屬性來控制其行為。

          隱藏內(nèi)部結(jié)構(gòu)是必要的。其他組件沒必要知道或也不依賴組件的內(nèi)部結(jié)構(gòu)或?qū)崿F(xiàn)細(xì)節(jié)。

          React 組件可能是函數(shù)組件或類組件、定義實(shí)例方法、設(shè)置 ref、擁有 state 或使用生命周期方法。這些實(shí)現(xiàn)細(xì)節(jié)被封裝在組件內(nèi)部,其他組件不應(yīng)該知道這些細(xì)節(jié)。

          隱藏內(nèi)部結(jié)構(gòu)的組件彼此之間的依賴性較小,而降低依賴度會帶來松耦合的好處。

          通信

          細(xì)節(jié)隱藏是隔離組件的關(guān)鍵。此時,你需要一種組件通信的方法:propsporps 是組件的輸入。

          建議 prop 的類型為基本數(shù)據(jù)(例如,stringnumberboolean):

          <Message text="Hello world!" modal={false} />;

          必要時,使用復(fù)雜的數(shù)據(jù)結(jié)構(gòu),如對象或數(shù)組:

          <MoviesList items={['Batman Begins''Blade Runner']} />

          prop 可以是一個事件處理函數(shù)和異步函數(shù):

          <input type="text" onChange={handleChange} />

          prop 甚至可以是一個組件構(gòu)造函數(shù)。組件可以處理其他組件的實(shí)例化:

          function If({ component: Component, condition }{
              return condition ? <Component /> : null;
          }
          <If condition={false} component={LazyComponent} />  

          為了避免破壞封裝,請注意通過 props 傳遞的內(nèi)容。給子組件設(shè)置 props 的父組件不應(yīng)該暴露其內(nèi)部結(jié)構(gòu)的任何細(xì)節(jié)。例如,使用 props 傳輸整個組件實(shí)例或 refs 都是一個不好的做法。

          訪問全局變量同樣也會對封裝產(chǎn)生負(fù)面影響。

          案例研究:封裝修復(fù)

          組件的實(shí)例和狀態(tài)對象是封裝在組件內(nèi)部的實(shí)現(xiàn)細(xì)節(jié)。因此,將狀態(tài)管理的父組件實(shí)例傳遞給子組件會破壞封裝。

          我們來研究一下這種情況。

          一個簡單的應(yīng)用程序顯示一個數(shù)字和兩個按鈕。第一個按鈕增加數(shù)值,第二個按鈕減少數(shù)值:


          核心代碼:

          class App extends React.Component {
              constructor(props) {
                  super(props);
                  this.state = { number0 };
              }

              render() {
                  return (
                      <div className="app">
                          <span className="number">{this.state.number}</span>
                          <Controls parent={this} />
                      </div>
                  );
              }
          }

          class Controls extends React.Component {
              render() {
                  return (
                      <div className="controls">
                          <button onClick={() => this.updateNumber(+1)}>
                              Increase
                    </button>
                          <button onClick={() => this.updateNumber(-1)}>
                              Decrease
                    </button>
                      </div>
                  );
              }

              updateNumber(toAdd) {
                  this.props.parent.setState(prevState => ({
                      number: prevState.number + toAdd
                  }));
              }
          }

          ReactDOM.render(<App />, document.getElementById('root')); 

          <Controls> 負(fù)責(zé)渲染按鈕,并為其設(shè)置事件處理函數(shù),當(dāng)用戶點(diǎn)擊按鈕時,父組件的狀態(tài)將會被更新:number 加1或者減1 (updateNumber()方法)

          當(dāng)前的實(shí)現(xiàn)有什么問題?

          • 第一個問題是:<App> 的封裝被破壞,因?yàn)樗膬?nèi)部結(jié)構(gòu)在應(yīng)用中傳遞。<App> 錯誤地允許 <Controls> 直接去修改其 state

          • 第二個問題是: 子組件 Controls 知道了太多父組件 的內(nèi)部細(xì)節(jié),它可以訪問父組件的實(shí)例,知道父組件是一個有狀態(tài)組件,知道父組件的 state 對象的細(xì)節(jié)(知道 number 是父組件 state 的屬性),并且知道怎么去更新父組件的 state.

          // 問題: 使用父組件的內(nèi)部結(jié)構(gòu)
          class Controls extends Component {
              render() {
                  return (
                      <div className="controls">
                          <button onClick={() => this.updateNumber(+1)}>
                              Increase
                    </button>
                          <button onClick={() => this.updateNumber(-1)}>
                              Decrease
                    </button>
                      </div>

                  );
              }

              updateNumber(toAdd) {
                  this.props.parent.setState(prevState => ({
                      number: prevState.number + toAdd
                  }));
              }
          }

          這樣就會導(dǎo)致:<Controls> 將很難測試和重用。對 <App> 結(jié)構(gòu)的細(xì)微修改會導(dǎo)致需要對 <Controls> 進(jìn)行修改(對于更大的應(yīng)用程序,也會導(dǎo)致類似耦合的組件需要修改)。

          解決方案是設(shè)計一個方便的通信接口,考慮到松耦合和封裝。讓我們改進(jìn)兩個組件的結(jié)構(gòu)和屬性,以便恢復(fù)封裝。

          只有組件本身應(yīng)該知道它的狀態(tài)結(jié)構(gòu)。<App> 的狀態(tài)管理應(yīng)該從 <Controls>updateNumber()方法)移到正確的位置:即 <App> 組件中。

          <App> 被修改為 <Controls> 設(shè)置屬性 onIncreaseonDecrease。這些是更新 <App> 狀態(tài)的回調(diào)函數(shù):

          // 解決: 恢復(fù)封裝
          class App extends Component {
              constructor(props) {
                  super(props);
                  this.state = { number0 };
              }

              render() {
                  return (
                      <div className="app">
                          <span className="number">{this.state.number}</span>
                          <Controls
                              onIncrease={() =>
           this.updateNumber(+1)}
                              onDecrease={() => this.updateNumber(-1)}
                          />
                      </div>
                  );
              }

              updateNumber(toAdd) {
                  this.setState(prevState => ({
                      number: prevState.number + toAdd
                  }));
              }
          }

          現(xiàn)在,<Controls> 接收用于增加和減少數(shù)值的回調(diào),注意解耦和封裝恢復(fù)時:<Controls> 不再需要訪問父組件實(shí)例。也不會直接去修改父組件的狀態(tài)。

          而且,<Controls> 被修改為了一個函數(shù)式組件:

          // 解決方案: 使用回調(diào)函數(shù)去更新父組件的狀態(tài)
          function Controls({ onIncrease, onDecrease }{
              return (
                  <div className="controls">
                      <button onClick={onIncrease}>Increase</button>
                      <button onClick={onDecrease}>Decrease</button>
                  </div>

              );
          }

          <App> 組件的封裝已經(jīng)恢復(fù),狀態(tài)由其本身管理,也應(yīng)該如此。

          此外,<Controls> 不在依賴 <App> 的實(shí)現(xiàn)細(xì)節(jié),onIncreaseonDecrease 在按鈕被點(diǎn)擊的時候調(diào)用,<Controls> 不知道(也不應(yīng)該知道)這些回調(diào)的內(nèi)部實(shí)現(xiàn)。

          <Controls> 組件的可重用性和可測試性顯著增加。

          <Controls> 的復(fù)用變得很容易,因?yàn)樗诵枰卣{(diào),沒有其它依賴。測試也變得簡單,只需驗(yàn)證單擊按鈕時,回調(diào)是否執(zhí)行。

          如果你喜歡探討技術(shù),或者對本文有任何的意見或建議,非常歡迎加魚頭微信好友一起探討,當(dāng)然,魚頭也非常希望能跟你一起聊生活,聊愛好,談天說地。

          魚頭的微信號是:krisChans95 也可以掃碼關(guān)注公眾號,訂閱更多精彩內(nèi)容。


          瀏覽 56
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚州无线一区欧美国产日产 | 人人操人人人 | 日韩美女逼逼 | 亚洲最大黄色视频 | 91视频成人 |