<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」之組件邏輯復(fù)用小技巧

          共 6680字,需瀏覽 14分鐘

           ·

          2020-12-31 22:45

          編者薦語:

          本文將介紹React組件邏輯復(fù)用的一些常用模式和技巧。包括一下幾個方面:

          • 什么是高階組件HOC
          • HOC解決了哪些問題
          • 如何封裝一個簡單的高階組件
          • HOC在項目中常用的一些技巧和方法
          • 什么是Render Props
          • Render Props的特點(diǎn)和用法
          • Render PropsHOC React Hooks相比,有哪些優(yōu)劣(重要面試題)

          HOC高階組件

          高階組件(HOC):是React中用于復(fù)用組件邏輯的一種高級技巧HOC自身不是React API的一部分,它是一種基于React的組合特性而形成的設(shè)計模式

          高階組件可以看做React裝飾器模式的一種實(shí)現(xiàn),具體而言,高階組件是參數(shù)作為組件,返回值為新組件的函數(shù)

          HOC解決的問題

          • 抽離公共組件,實(shí)現(xiàn)組件代碼復(fù)用,常見場景:頁面復(fù)用。
          • 條件渲染,控制組件的渲染邏輯(渲染劫持),常見場景:權(quán)限控制。
          • 捕獲/劫持被處理組件的生命周期,常見場景:組件渲染性能追蹤、日志打點(diǎn)。

          當(dāng)我們項目中使用高階組件開發(fā)時,能夠讓代碼變得更加優(yōu)雅,同時增強(qiáng)代碼的復(fù)用性和靈活性,提升開發(fā)效率

          高階組件的基本框架

          高階組件的框架:

          export?default?(WrappedComponent)?=>?{
          ?return?class?NewComponent?extends?React.Component?{
          ??//?可以自定義邏輯
          ????//?比如給?WrappedComponent組件傳遞props和methods
          ????render?()?{
          ???return?<WrappedComponent?{...this.props}/>
          ????}
          ??}
          }

          如果自定義了statemethods可以通過下面方式傳遞到子組件中

          export?default?(WrappedComponent)?=>?{
          ?return?class?NewComponent?extends?React.Component?{
          ??state?=?{
          ??????markTime:?new?Date().toLocaleTimeString();?//?獲取組件當(dāng)前渲染時的時間
          ????}
          ????printTime()?{
          ??????let?myDate?=?new?Date();
          ??????let?myTime=?myDate.toLocaleTimeString();?
          ??????console.log('當(dāng)前時間',?myTime)
          ????}
          ????render?()?{
          ???return?<WrappedComponent?markTime={this.state.markTime}?printTime={this.printTime}/>
          ????}
          ??}
          }

          這樣在WrappedComponent組件中,如果是類組件就可以通過this.props.markTime獲取,函數(shù)組件的話通過props.markTime來獲取,方法獲取和狀態(tài)獲取相同。

          HOC可以做什么

          屬性代理——可操作所有傳入的props

          可以讀取、添加、編輯、刪除傳給 WrappedComponent 的 props(屬性)

          「場景描述」:Hello組件傳遞show,hide方法,讓其顯示Loading加載框

          const?loading?=?message?=>?OldComponent?=>?{
          ??return?class?extends?React.Component?{
          ????//?顯示一個?Loading的div
          ????state?=?{
          ??????show:?()?=>?{
          ????????let?div?=?document.createElement('div');
          ????????div.innerHTML?=?`${message}

          `

          ????????document.body.appendChild(div);
          ??????},
          ??????hide:?()?=>?{
          ????????document.getElementById('loading').remove();
          ??????}
          ????}
          ????render()?{
          ??????return?(
          ????????<div>
          ??????????<OldComponent?{...this.props}?{...this.state}/>
          ????????div>

          ??????)
          ????}
          ??}
          }
          function?Hello(props)?{
          ??return?(
          ????<div>hello
          ??????<button?onClick={props.show}>showbutton>
          ??????<button?onClick={props.hide}>hidebutton>
          ????div>
          ??)
          }

          let?HightLoadingHello?=?loading('正在加載')(Hello);
          ReactDom.render(<HightLoadingHello/>,?document.getElementById('root'));

          效果如圖:

          抽離公共組件,最大化實(shí)現(xiàn)復(fù)用

          「場景描述」:統(tǒng)計每個組件的渲染時間

          class?CalTimeComponent?extends?React.Component?{
          ??componentWillMount()?{
          ????this.start?=?Date.now();?//?初始渲染節(jié)點(diǎn)
          ??}
          ??componentDidMount()?{
          ????console.log((Date.now()?-?this.start)?+?'ms');
          ??}
          ??render()?{
          ????return?<div>calTimeComponentdiv>
          ??}
          }

          ReactDom.render(<CalTimeComponent/>,?document.getElementById('root'));

          這樣僅僅能計算當(dāng)前組件的渲染時間,假如現(xiàn)在有這樣一個需求,需要統(tǒng)計每個組件的渲染時間呢?

          就應(yīng)該想到把它抽離出去,比如:

          //?CalTimeComponent.js
          export?default?function?CalTimeComponent(OldComponent)?{
          ??return?class?extends?React.Component?{
          ????state?=?{
          ??????markTime:?new?Date().toLocaleTimeString()
          ????}
          ????componentWillMount()?{
          ??????this.start?=?Date.now();
          ????}
          ????componentDidMount()?{
          ??????console.log((Date.now()?-?this.start)?+?'ms');
          ????}
          ????printTime()?{
          ??????let?myDate?=?new?Date();
          ??????let?myTime=?myDate.toLocaleTimeString();?
          ??????console.log('當(dāng)前時間',?myTime)
          ????}
          ????render()?{
          ??????return?<OldComponent?markTime={this.state.markTime}?printTime={this.printTime}/>
          ????}
          ??}
          }

          //?HelloComponent.js
          import?withTracker?from?'../../Components/CalTimeComponent.js';

          class?HelloComponent?extends?React.Component{
          ??render()?{
          ????console.log(this.props);
          ????this.props.printTime()
          ????return?<div>hellodiv>

          ??}
          }

          let?HighHelloComponent?=?CalTimeComponent(HelloComponent);
          ReactDom.render(<HighHelloComponent/>,?document.getElementById('root'));

          這樣就能最大化的實(shí)現(xiàn)CalTimeComponent組件復(fù)用了,把它引入到想要計算時間的組件里,并傳入當(dāng)前組件就好了。

          Render Props

          特點(diǎn)1render props指在一種React組件之間使用一個值為函數(shù)的props共享代碼的簡單技術(shù)。

          特點(diǎn)2:具有render props的組件接收一個函數(shù),該函數(shù)返回一個React元素并調(diào)用它而不是實(shí)現(xiàn)一個自己的渲染邏輯。

          特點(diǎn)3render props是一個用于告知組件需要渲染什么內(nèi)容的函數(shù)(props)

          特點(diǎn)4:也是組件邏輯復(fù)用的一種實(shí)現(xiàn)方式

          接下來,我通過一個例子帶大家分別認(rèn)識上面的四種特點(diǎn)

          「場景描述」: 在多個組件內(nèi)實(shí)時獲取鼠標(biāo)的x、y坐標(biāo)

          原生實(shí)現(xiàn):不復(fù)用邏輯

          class?MouseTracker?extends?React.Component?{
          ??constructor(props)?{
          ????super(props);
          ????this.state?=?{
          ??????x:?0,
          ??????y:?0,
          ????}
          ??}
          ??handleMouseMove?=?(event)?=>?{
          ????this.setState({
          ??????x:?event.clientX,
          ??????y:?event.clientY
          ????});
          ??}
          ??render()?{
          ????return?(
          ??????<div?onMouseMove={this.handleMouseMove}>
          ????????<h1>請移動鼠標(biāo)h1>

          ????????<p>當(dāng)前鼠標(biāo)的位置是:x:{this.state.x} y:{this.state.y}p>
          ??????div>
          ????)
          ??}
          }

          ReactDom.render(<MouseTracker/>,?document.getElementById('root'));

          上面,這是在一個組件內(nèi)完成的,假如現(xiàn)在要在多個div內(nèi)完成上面的邏輯該怎么辦,就該想到復(fù)用了,看看render prop是怎么幫我們完成的?

          Render Props

          class?MouseTracker?extends?React.Component?{
          ??constructor(props)?{
          ????super(props);
          ????this.state?=?{
          ??????x:?0,
          ??????y:?0,
          ????}
          ??}
          ??handleMouseMove?=?(event)?=>?{
          ????this.setState({
          ??????x:?event.clientX,
          ??????y:?event.clientY
          ????});
          ??}
          ??render()?{
          ????console.log(this.props)
          ????return?(
          ??????<div?onMouseMove={this.handleMouseMove}>
          ????????{this.props.render(this.state)}
          ??????div>

          ????)
          ??}
          }

          ReactDom.render(
          <MouseTracker?render={
          ??props?=>
          ?(
          ????<React.Fragment>
          ??????<h1>請移動鼠標(biāo)h1>

          ??????<p>當(dāng)前鼠標(biāo)的位置是:?x:{props.x}?y:{props.y}p>
          ????React.Fragment>
          ??)
          }>MouseTracker>,?document.getElementById('root'));

          注意:render props 是因?yàn)槟J讲疟环Q為 render props ,你不一定要用名為 renderprops 來使用這種模式。render props 是一個用于告知組件需要渲染什么內(nèi)容的函數(shù) `prop

          那如果改寫成高階組件呢?

          高階組件寫法

          改寫成高階組件,并將公共組件抽離出去, ShowPosition子組件中可以拿到withTracker父組件中傳遞的x、y坐標(biāo)值

          //?withTracker.js
          export?default?function?withTracker?(OldComponent)?{
          ??return?class?MouseTracker?extends?React.Component?{
          ????constructor(props)?{
          ??????super(props);
          ??????this.state?=?{
          ????????x:?0,
          ????????y:?0,
          ??????}
          ????}
          ????handleMouseMove?=?(event)?=>?{
          ??????this.setState({
          ????????x:?event.clientX,
          ????????y:?event.clientY
          ??????});
          ????}
          ????render()?{
          ??????console.log(this.props)
          ??????return?(
          ????????<div?onMouseMove={this.handleMouseMove}>
          ??????????<OldComponent?{...this.state}/>
          ????????div>

          ??????)
          ????}
          ??}
          }

          //?ShowPosition.js
          import?withTracker?from?'../../Components/withTracker.js';

          function?ShowPosition(props)?{
          ??return?(
          ????<React.Fragment>
          ??????<h1>請移動鼠標(biāo)h1>
          ??????<p>當(dāng)前鼠標(biāo)的位置是:?x:{props.x}?y:{props.y}p>
          ????React.Fragment>
          ??)
          }

          //?在?ShowPosition?組件中?可以拿到?withTracker?傳遞過來的坐標(biāo)值
          let?HightShowPosition?=?withTracker(ShowPosition);

          ReactDom.render(<HightShowPosition/>,?document.getElementById('root'));

          hoc、render props、react-hooks的優(yōu)劣如何?

          HOC的優(yōu)勢:

          • 抽離公共組件,實(shí)現(xiàn)組件代碼復(fù)用,常見場景:頁面復(fù)用。
          • 條件渲染,控制組件的渲染邏輯(渲染劫持),常見場景:權(quán)限控制。
          • 捕獲/劫持被處理組件的生命周期,常見場景:組件渲染性能追蹤、日志打點(diǎn)。
          • 屬性代理,可以給一些子組件傳遞層次比較遠(yuǎn)的屬性,并按需求操作他們

          HOC的缺陷:

          • 擴(kuò)展性限制HOC無法從外部訪問子組件(被包裹組件WrappedComponent)的state,因此無法通過shouldComponentUpdate過濾掉不必要的更新(React支持ES6之后,提供了React.pureComponent來解決這個問題)
          • Ref傳遞問題Ref由于組件被高階組件包裹,導(dǎo)致被隔斷,需要后來的React.forwardRef來解決這個問題
          • 層級嵌套HOC可能出現(xiàn)多層包裹組件的情況(一般不超過兩層,否則不好維護(hù))多層抽象增加了復(fù)雜度和理解成本
          • 命名沖突:如果高階組件多次嵌套,沒有使用命名空間的話會產(chǎn)生沖突,覆蓋老屬性

          Render Props優(yōu)點(diǎn):

          • 上述HOC的缺點(diǎn),Render Props都可以解決

          Render Props缺陷:

          • 使用繁瑣HOC只需要借助裝飾器/高階函數(shù)的特點(diǎn)就可以進(jìn)行復(fù)用,而Render Props需要借助回調(diào)嵌套
          • 嵌套過深Render Props雖然擺脫了組件多層嵌套的問題,但是轉(zhuǎn)化為了函數(shù)回調(diào)的嵌套

          React Hooks的優(yōu)點(diǎn)(重點(diǎn)):

          • 簡潔React Hooks解決了HOCRender Props的嵌套問題,更加簡潔
          • 解耦React Hooks可以更方便地把 UI 和狀態(tài)分離,做到更徹底的解耦
          • 無影響復(fù)用組件邏輯Hook 使你在無需修改組件結(jié)構(gòu)的情況下復(fù)用狀態(tài)邏輯
          • 函數(shù)友好:React Hooks為函數(shù)組件而生,從而解決了類組件的幾大問題
            • this 指向容易錯誤
            • 分割在不同聲明周期中的邏輯使得代碼難以理解和維護(hù)
            • 代碼復(fù)用成本高(高階組件容易使代碼量劇增)

          React Hooks的缺陷(重點(diǎn)):

          • 額外的學(xué)習(xí)成本(Functional Component 與 Class Component 之間的困惑)
          • 寫法上有限制(不能出現(xiàn)在條件、循環(huán)中,只能在最外層調(diào)用Hooks),并且寫法限制增加了重構(gòu)成本
          • 破壞了PureComponent、React.memo淺比較的性能優(yōu)化效果(為了取最新的props和state,每次render()都要重新創(chuàng)建事件處函數(shù))(依賴項不變,可解決該問題)
          • 使用不當(dāng),可能會造成閉包陷阱問題
          • React.memo并不能完全替代shouldComponentUpdate(因?yàn)槟貌坏?state change,只針對 props change)

          關(guān)于react-hooks的評價來源于官方react-hooks RFC

          最后



          如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:

          1. 點(diǎn)個「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點(diǎn)在看,都是耍流氓 -_-)

          2. 歡迎加我微信「qianyu443033099」拉你進(jìn)技術(shù)群,長期交流學(xué)習(xí)...

          3. 關(guān)注公眾號「前端下午茶」,持續(xù)為你推送精選好文,也可以加我為好友,隨時聊騷。


          點(diǎn)個在看支持我吧,轉(zhuǎn)發(fā)就更好了


          瀏覽 52
          點(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 | 精品无码av一区二区三区不卡 | 69国产精品久久久久久人 | 天堂色综合 | 国产三级片视频网站 |