第一個 React 項目作完了,談談本身對 hooks 的思考

轉自:zouwowo前端

juejin.cn/post/6907189103151087623vue

react

本文不會有React具體應用的內容,只是一些對於hooks跟以前的類組件的比較,對於hooks自己的思考。web

筆者在今年的8月份入職如今的公司,從原來的vue轉爲React。由於公司還存在一些比較老的項目,因此前期並無徹底投入到React的項目開發當中。從10月份開始,參與了一個公司層面的新項目從0到1的構建過程,這也是我第一個React項目。咱們是全面擁抱hooks的,這個項目的開發過程當中,我也寫了不少自定義的hooks方法,封裝了好幾個通用的功能組件,也算是熟練了React的具體應用。編程

項目開發初期,我一直有個疑惑,我在入職以前學習React的時候,其實不少的教程都是使用的類組件的方法,不多看到hooks的相關教程。本身邊學邊寫demo的時候使用的也都是類組件的寫法,可是如今愈來愈多的團隊開始全面擁抱hookshooks到底有什麼優勢?我將在下面提出一些本身的思考和想法。api

類組件和函數組件區別

這裏要注意,類組件hooks,這兩個東西其實並非一個概念。hooks只是一個工具集,用來加強函數組件的功能。真正要對比的應該是類組件函數組件數組

咱們先來看看類組件函數組件的區別。性能優化

代碼寫法上的區別

這是最直觀的區別,代碼就長的不同嘛。我隨便列幾個很常見的例子,這些特性在函數組件裏都沒有。微信

  • 類組件,顧名思義,它就是一個類,須要繼承 Class
  • 類組件能夠直接定義state
  • 類組件有生命週期方法
  • 類組件可使用this獲取到組件實例

心智模型上的區別

這是兩個組件之間最大的區別,用https://overreacted.io/zh-hans/how-are-function-components-different-from-classes/中的話來講。閉包

函數式組件捕獲了渲染所用的值。

咱們引用文章裏的一個例子

function ProfilePage(props{
  const showMessage = () => {
    alert('成功關注 ' + props.user);
  };

  const handleClick = () => {
    setTimeout(showMessage, 3000);
  };

  return (
    <button onClick={handleClick}>關注</button>
  );
}
複製代碼
class ProfilePage extends React.Component {
  showMessage = () => {
    alert('成功關注 ' + this.props.user);
  };

  handleClick = () => {
    setTimeout(this.showMessage, 3000);
  };

  render() {
    return <button onClick={this.handleClick}>關注</button>;
  }
}
複製代碼

咱們用類組件以及函數組件實現了同一個邏輯。這兩個組件都會接收一個props.user的屬性,咱們點擊按鈕,在3秒以後,會alert一條成功關注的信息。

假如一開始傳入的props的值是 { user: '帥wowo' },而後咱們點擊關注按鈕,在3秒以內,傳入的props值變化了,變成了{ user: '醜wowo' }。這兩個組件將分別alert出什麼內容?

有過經驗的同窗確定能輕鬆答出來。

// 函數組件會打印
'成功關注 帥wowo'
// 類組件會打印
'成功關注 醜wowo'
複製代碼

爲何會這樣呢?(這裏註明一下,這個例子跟React框架無關,只是js的基本特性,你在任何用js編寫的代碼中均可以復現)

React的類組件中,props雖然是不變的,可是this永遠是可變。當有異步的事件觸發,它獲取到的props或者state永遠都是最新的。固然咱們也有辦法去解決。

好比咱們能夠從新定義一個數據來保存`props

handleClick = () => {
  const {user} = this.props;
  setTimeout(() => this.showMessage(user), 3000);
};
複製代碼

但這種方式太過繁瑣,各類定義的數據很是不夠優雅。

或者把事件都寫到渲染函數render

class ProfilePage extends React.Component {
  render() {
    const props = this.props;

    const showMessage = () => {
      alert('成功關注 ' + props.user);
    };

    const handleClick = () => {
      setTimeout(showMessage, 3000);
    };

    return <button onClick={handleClick}>關注</button>;
  }
}
複製代碼

這個方法其實函數組件的原理,props變化以後,組件雖然從新渲染了,可是老的props經過閉包保存了下來,而後被打印出來。

寫了這麼多,只是爲了論證那句話,函數式組件捕獲了渲染所用的值。

爲何咱們要使用函數組件+hooks

這一點不少人可能以爲不必,以爲官方出的東西,跟着用就好,寫着也挺順手的,還管啥爲何呢?

關於這一點,我以爲最重要的其實就是學習大佬們的思惟,爲何要作出一個hooks來?確定是爲了解決一些原來的開發過程當中的問題。React團隊的設計層面的思路,可以在必定程度上表明當前業界在框架設計領域裏最佳實踐。

接下來我會列出幾個我認爲的類組件的幾個痛點。(好和壞都是比較出來的,這些痛點只是相比於函數組件+hooks而言,技術在不斷髮展,技術的迭代都是正常的趨勢)

1.函數組件的寫法更輕量,更加靈活

在函數組件中,咱們不須要去繼承一個class對象,不須要記憶那些生命週期,不須要固定的把數據定義在state中。函數做爲js中的一等公民,函數式編程方式可讓咱們能夠更加靈活的去組織代碼。

2.類組件存在自身的缺陷

一個其實就是上面一節寫到,若是咱們須要一個只跟着視圖走的數據,咱們不能直接使用props或者state

還有一個是最多見的,在React中,若是咱們定義一個方法,咱們必須使用bind或者箭頭函數去約束這個方法的this的做用域。

這兩個問題雖然咱們都能解決,可是本質上,都是咱們經過代碼實踐的方式去解決類組件自身的缺陷。

可是在函數組件中,咱們不會有這種問題,經過閉包的方式,在一次渲染中,組件的propsstate是保持不變的,並且傳遞的方法自己就是已經被約束做用域了。

3.邏輯是分散的,並且難以複用

這個痛點其實跟vue2是如出一轍的,React的類組件和vue2的開發模式都是相似。

我拿一張尤大來談論vue2vue3區別時候的一張圖來舉例

這裏的不一樣顏色其實就是不一樣邏輯,這圖能夠分紅數據,事件方法,生命週期,模板四塊內容。

咱們能夠看到,一個邏輯在類組件裏實際上是分散的,拿React來講,數據須要定義在state裏,而後須要編寫相關的事件方法,再在生命週期裏進行邏輯的初始化,組件更新時候的處理,最後在模板裏寫jsx

咱們若是是去維護這個代碼,會很痛苦,爲了查看一個邏輯,咱們要上下翻,找出各自的數據,方法,生命週期和模板。

並且這種方式的代碼,很難被複用,抽離重複邏輯咱們經歷過mixinHOC & render-props。這兩種方式都有一個最大的問題,那就是props的來源不夠清晰。mixin就不說了,都是淚,只能一個個去找。HOC嵌套層級一多,也會很難肯定來源,並且HOC的學習成本也相對比較高,對於新手不太友好。(這塊是按我vue2開發用到的mixinHOC的經驗寫的,我做爲React的新手,並無經歷過ReactmixinHOC抽離邏輯的時代,因此若是寫的有問題,請各位大佬指出。最後感嘆一句,ReactHOC可太方便了!)

可是若是咱們使用hooks,我仍是用一張vue3中組合式API的圖來講明,跟使用hooks的結果是相似的。

每塊邏輯都是一個總體,很是清晰明瞭。並且你能夠把這些邏輯都抽離出去,只是在這個組件當中引用便可。

邏輯複用就不用說了,如今誰家React項目裏沒有好多自定義的hooks啊。

4.hooks更貼合React的基本理念

React的設計理念 裏第一條

React的核心理念之一,相同的參數輸入應該產生相同的輸出。簡單說,它應當是一個簡單的純函數。

咱們能夠拿以前說到的心智模型上的區別中的例子來講明,咱們傳入一個props參數{ user: '帥wowo' },咱們但願關於這個參數的全部事件都強依賴於這條數據。它不該該像類組件同樣,傳入的參數和咱們獲得的輸出不一致。

可是函數組件能夠作到,重複引用一下上面的一句話,在一次渲染中,組件的propsstate是保持不變的

hooks的不足

談論一個技術,咱們不能太過於片面,必定要保持辯證思惟,理性分析。上面說了不少hooks的優勢,可是它依然存在着不足。

1.比較大的心智負擔

一樣是由於上面那句話,在一次渲染中,組件的propsstate是保持不變的。這個特性致使的閉包陷阱是咱們如今開發中最多見的一個問題。咱們須要時刻注意是否已經給hooks添加了必要的依賴項。在封裝一些功能相對複雜的組件時,useEffect的重複渲染問題處理有時候會很是棘手,並且不易調試。

這個特性在對函數組件進行性能優化時也是會帶來很大的麻煩,由於每次propsstate數據變化,都會致使函數組件中全部內容的從新渲染。咱們須要經過memouseMemouseCallback這些方法手動去減小組件的render。當一個組件結構比較複雜,嵌套較多時,依賴項問題的處理也很讓人頭疼。具體性能優化的內容,能夠看我以前的文章。

這些點給開發者帶來的學習hooks的成本相比較類組件來講會更大。

2.須要更嚴格的開發規範

剛纔咱們說了,函數式編程可以給咱們帶來很大的靈活度,這個靈活度在開發當中是一個雙刃劍。它能幫助咱們更好的去解決一些複雜的需求,可是它在一個多人協做開發的項目中並非一個好的事情。

一些大型的項目,最重要的必定是編程的規範,eslint能夠限制語法規範,可是限制不了咱們實現需求的規範,一人一個風格的代碼對於一個項目來講就是個災難,對於後續的維護,公共方法的抽離來講將帶來很大的麻煩。

因此,須要開發者制定一個具體的開發規範,並在開發的時候嚴格遵照。

3.hooks並不能徹底代替類組件

雖然咱們有了不少hooks方法,用來加強函數組件的功能。好比useState可讓函數組件維護本身的數據。有useEffect能夠在必定程度上彌補函數組件沒有生命週期的缺點。

注意:useEffect並非用來代替生命週期,它只是提供了一個相似生命週期的方法。二者其實本質上沒有可比性。

想了解更多,能夠看Dan的這篇博客,https://overreacted.io/zh-hans/a-complete-guide-to-useeffect/,它能夠幫你更好的理解hooks中的useEffect

可是,咱們仍是不能拿hooks來徹底代替類組件。(主要是部分生命週期還沒法被代替)

好比componentDidCatch這個獲取組件錯誤的生命週期,在大部分的項目中,確定會看到它的身影。還有其餘的一些生命週期,可能你在一些特殊的場景下仍是須要用到。

因此,在一段時間以內,hooks仍是會跟類組件共存。

總結

愈來愈多的公司和團隊早已經全面擁抱hooks,如今再談論使用hooks的收益跟付出相比是否值得已經毫無心義,大的潮流已經給你指明瞭方向,今年新發布的正式版vue3也是借鑑了hooks的方式,實現了讓開發者使用組合式api的方式組織邏輯代碼。大勢所趨,你也得趕忙跟上。

感謝,求贊

感謝您的閱讀,若是以爲本文對你有所幫助的話,請動手點個贊支持一下,謝謝。


點贊和在看就是最大的支持❤️

本文分享自微信公衆號 - 前端技術江湖(bigerfe)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索