轉自:zouwowo前端
juejin.cn/post/6907189103151087623vue
前言react
本文不會有React
具體應用的內容,只是一些對於hooks
跟以前的類組件的比較,對於hooks
自己的思考。web
筆者在今年的8月份入職如今的公司,從原來的vue
轉爲React
。由於公司還存在一些比較老的項目,因此前期並無徹底投入到React
的項目開發當中。從10月份開始,參與了一個公司層面的新項目從0到1的構建過程,這也是我第一個React
項目。咱們是全面擁抱hooks
的,這個項目的開發過程當中,我也寫了不少自定義的hooks
方法,封裝了好幾個通用的功能組件,也算是熟練了React
的具體應用。編程
項目開發初期,我一直有個疑惑,我在入職以前學習React
的時候,其實不少的教程都是使用的類組件的方法,不多看到hooks
的相關教程。本身邊學邊寫demo的時候使用的也都是類組件的寫法,可是如今愈來愈多的團隊開始全面擁抱hooks
,hooks
到底有什麼優勢?我將在下面提出一些本身的思考和想法。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
的做用域。
這兩個問題雖然咱們都能解決,可是本質上,都是咱們經過代碼實踐的方式去解決類組件
自身的缺陷。
可是在函數組件中,咱們不會有這種問題,經過閉包的方式,在一次渲染中,組件的props
和state
是保持不變的,並且傳遞的方法自己就是已經被約束做用域了。
3.邏輯是分散的,並且難以複用
這個痛點其實跟vue2
是如出一轍的,React
的類組件和vue2
的開發模式都是相似。
我拿一張尤大來談論vue2
和vue3
區別時候的一張圖來舉例
這裏的不一樣顏色其實就是不一樣邏輯,這圖能夠分紅數據,事件方法,生命週期,模板四塊內容。
咱們能夠看到,一個邏輯在類組件裏實際上是分散的,拿React
來講,數據須要定義在state
裏,而後須要編寫相關的事件方法,再在生命週期裏進行邏輯的初始化,組件更新時候的處理,最後在模板裏寫jsx
。
咱們若是是去維護這個代碼,會很痛苦,爲了查看一個邏輯,咱們要上下翻,找出各自的數據,方法,生命週期和模板。
並且這種方式的代碼,很難被複用,抽離重複邏輯咱們經歷過mixin
,HOC & render-props
。這兩種方式都有一個最大的問題,那就是props
的來源不夠清晰。mixin
就不說了,都是淚,只能一個個去找。HOC
嵌套層級一多,也會很難肯定來源,並且HOC
的學習成本也相對比較高,對於新手不太友好。(這塊是按我vue2
開發用到的mixin
和HOC
的經驗寫的,我做爲React
的新手,並無經歷過React
的mixin
和HOC
抽離邏輯的時代,因此若是寫的有問題,請各位大佬指出。最後感嘆一句,React
用HOC
可太方便了!)
可是若是咱們使用hooks
,我仍是用一張vue3
中組合式API的圖來講明,跟使用hooks
的結果是相似的。
![](http://static.javashuo.com/static/loading.gif)
每塊邏輯都是一個總體,很是清晰明瞭。並且你能夠把這些邏輯都抽離出去,只是在這個組件當中引用便可。
邏輯複用就不用說了,如今誰家React
項目裏沒有好多自定義的hooks
啊。
4.hooks更貼合React的基本理念
React的設計理念 裏第一條
React的核心理念之一,相同的參數輸入應該產生相同的輸出。簡單說,它應當是一個簡單的純函數。
咱們能夠拿以前說到的心智模型上的區別中的例子來講明,咱們傳入一個props
參數{ user: '帥wowo' }
,咱們但願關於這個參數的全部事件都強依賴於這條數據。它不該該像類組件
同樣,傳入的參數和咱們獲得的輸出不一致。
可是函數組件能夠作到,重複引用一下上面的一句話,在一次渲染中,組件的props
和state
是保持不變的。
hooks的不足
談論一個技術,咱們不能太過於片面,必定要保持辯證思惟,理性分析。上面說了不少hooks
的優勢,可是它依然存在着不足。
1.比較大的心智負擔
一樣是由於上面那句話,在一次渲染中,組件的props
和state
是保持不變的。這個特性致使的閉包陷阱
是咱們如今開發中最多見的一個問題。咱們須要時刻注意是否已經給hooks
添加了必要的依賴項。在封裝一些功能相對複雜的組件時,useEffect
的重複渲染問題處理有時候會很是棘手,並且不易調試。
這個特性在對函數組件
進行性能優化時也是會帶來很大的麻煩,由於每次props
和state
數據變化,都會致使函數組件
中全部內容的從新渲染。咱們須要經過memo
,useMemo
,useCallback
這些方法手動去減小組件的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源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。