日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

onclick=两个函数_[译]React函数组件和类组件的差异

發布時間:2025/3/20 编程问答 13 豆豆
生活随笔 收集整理的這篇文章主要介紹了 onclick=两个函数_[译]React函数组件和类组件的差异 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

[譯]React函數組件和類組件的差異

原文: https://overreacted.io/how-are-function-components-different-from-classes/

在 React.js 開發中,函數組件(function component) 和 類組件(class component) 有什么差異呢?

在以前,通常認為區別是,類組件提供了更多的特性(比如state)。隨著 React Hooks 的到來,這個說法也不成立了(通過hooks,函數組件也可以有state和類生命周期回調了)。

或許你也聽說過,這兩類組件中,有一類的性能更好。哪一類呢?很多這方面的性能測試,都是 有缺陷的 ,因此要從這些測試中 得出結論 ,不得不謹慎一點。性能主要取決于你代碼要實現的功能(以及你的具體實現邏輯),和使用函數組件還是類組件,沒什么關系。我們觀察發現,盡管函數組件和類組件的性能優化策略 有些不同 ,但是他們性能上的差異是很微小的。

不管是上述哪個原因,我們都 不建議 你使用函數組件重寫已有的類組件,除非你有別的原因,或者你喜歡當第一個吃螃蟹的人。React Hooks 還很新(就像2014年的React一樣),目前還沒有使用hooks相關的最佳實踐。

除了上面這些,還有什么別的差異么?在函數組件和類組件之間,真的存在根本性的不同么?"Of course, there are — in the mental model" (這個實在不知道咋表達,貼下作者原文吧 ) 在這篇文章里,我們將一起看下,這兩類組件最大的不同。這個不同點,在2015年函數組件函數組件 被引入React 時就存在了,但是被大多數人忽略了。

函數組件和類組件的差異

函數組件會捕獲render內部的狀態

讓我們一步步來看下,這代表什么意思。

注意,本文并不是對函數組件和類組件進行價值判斷。我只是展示下React生態里,這兩種組件編程模型的不同點。要學習如何更好的采用函數組件,推薦官方文檔 Hooks FAQ 。

假設我們有如下的函數組件:

function ProfilePage(props) {const showMessage = () => {alert('Followed ' + props.user);};const handleClick = () => {setTimeout(showMessage, 3000);};return (<button onClick={handleClick}>Follow</button>); }

組件會渲染一個按鈕,當點擊按鈕的時候,模擬了一個異步請求,并且在請求的回調函數里,顯示一個彈窗。比如,如果 props.user 的值是 Dan,點擊按鈕3秒之后,我們將看到 Followed Dan 這個提示彈窗。非常簡單。

(注意,上面代碼里,使用箭頭函數還是普通的函數,沒有什么區別。因為沒有 this 問題。把箭頭函數換成普通函數 function handleClick()沒有任何問題 )

我們怎樣實現同樣功能的類組件呢?很簡單的翻譯一下:

class ProfilePage extends React.Component {showMessage = () => {alert('Followed ' + this.props.user);};handleClick = () => {setTimeout(this.showMessage, 3000);};render() {return <button onClick={this.handleClick}>Follow</button>;} }

通常情況下,我們會認為上面兩個組件實現,是完全等價的。開發者經常像上面這樣,在函數組件和類組件之間重構代碼,卻沒有意識到他們隱含的差異。

但是,上面兩種實現,存在著微妙的差異 。再仔細看一看,你能看出其中的差異么?講真,還是花了我一段時間,才看出其中的差異。

如果你想在線看看源代碼,你可以 點擊這里 。 本文剩余部分,都是講解這個不同點,以及為什么不同點會很重要。

在我們繼續往下之前,我想再次說明下,本文提到的差異性,和react hooks本身完全沒有關系!上面例子里,我都沒用到hooks呢。

本文只是講解,react生態里,函數組件和類組件的差異性。如果你打算在react開發中,大規模的使用函數組件,那么你可能需要了解這個差異。

我們將使用在react日常開發中,經常遇到的一個bug,來展示這個差異 。

接下來,我們就來復現下這個bug。打開 這個在線例子 ,頁面上有一個名字下拉框,下面是兩個關注組件,一個是前文的函數組件,另一個是類組件。

對每一個關注按鈕,分別進行如下操作:

  • 點擊其中1個關注按鈕
  • 在3秒之內,重新選擇下拉框中的名字
  • 3秒之后,注意看alert彈窗中的文字差異
  • 你應該注意到了兩次alert彈窗的差別:

    • 在函數組件的測試情況下,下拉框中選中 Dan,點擊關注按鈕,迅速將下拉框切換到Sophie,3秒之后,alert彈窗內容仍然是 Followed Dan
    • 在類組件的測試情況下,重復相同的動作,3秒之后,alert彈窗將會顯示 Followed Sophie

    在這個例子里,使用函數組件的實現是正確的,類組件的實現明顯有bug。如果我先關注了一個人,然后切換到了另一個人的頁面,關注按鈕不應該混淆我實際關注的是哪一個 。

    (PS,我也推薦你真的關注下 Sophie)

    那么,為什么我們的類組件,會存在問題呢?

    讓我們再仔細看看類組件的 showMessage 實現:

    class ProfilePage extends React.Component {showMessage = () => {alert('Followed ' + this.props.user);};

    這個方法會讀取 this.props.user 。在React生態里,props是不可變數據,永遠不會改變。但是,this卻始終是可變的 。

    確實,this存在的意義,就是可變的。react在執行過程中,會修改this上的數據,保證你能夠在 render和其他的生命周期方法里,讀取到最新的數據(props, state)。

    因此,如果在網絡請求處理過程中,我們的組件重新渲染,this.props改變了。在這之后,showMessage方法會讀取到改變之后的 this.props。

    這揭示了用戶界面渲染的一個有趣的事實。如果我們認為,用戶界面(UI)是對當前應用狀態的一個可視化表達(UI=render(state)),那么事件處理函數,同樣屬于render結果的一部分,正如用戶界面一樣 。我們的事件處理函數,是屬于事件觸發時的render,以及那次render相關聯的 props 和state。

    然而,我們在按鈕點擊事件處理函數里,使用定時器(setTimeout)延遲調用 showMessage,打破了showMessage和this.props的關聯。showMessage回調不再和任何的render綁定,同樣丟失了本來關聯的props。從 this 上讀取數據,切斷了這種關聯。

    假如函數組件不存在,那我們怎么來解決這個問題呢?

    我們需要通過某種方式,修復showMessage和它所屬的 render以及對應props的關聯。

    一種方式,我們可以在按鈕點擊處理函數中,讀取當前的 props,然后顯式的傳給 showMessage,就像下面這樣:

    class ProfilePage extends React.Component {showMessage = (user) => {alert('Followed ' + user);};handleClick = () => {const {user} = this.props;setTimeout(() => this.showMessage(user), 3000);};render() {return <button onClick={this.handleClick}>Follow</button>;} }

    這種方式 可以解決這個問題 。然而,這個解決方式讓我們引入了冗余的代碼,隨著時間推移,容易引入別的問題。如果我們的 showMessage方法要讀取更多的props呢?如果showMessage還要訪問state呢?如果 showMessage 調用了其他的方法,而那個方法讀取了別的狀態,比如this.props.something 或 this.state.something ,我們會再次面臨同樣的問題。 我們可能需要在 showMessage 里顯式的傳遞 this.props this.state 給其他調用到的方法。

    這樣做,可能會破壞類組件帶給我們的好處。這也很難判斷,什么時候需要傳遞,什么時候不需要,進一步增加了引入bug的風險。

    同樣,簡單地把所有代碼都放在 onClick 處理函數里,會帶給我們其他的問題。為了代碼可讀性、可維護性等原因,我們通常會把大的函數拆分為一些獨立的小的函數。這個問題不僅僅是react才有,所有在this上維護可變數據的UI類庫,都很容易遇到這個問題。

    或許,我們可以在類的構造函數里綁定一些方法?

    class ProfilePage extends React.Component {constructor(props) {super(props);this.showMessage = this.showMessage.bind(this);this.handleClick = this.handleClick.bind(this);}showMessage() {alert('Followed ' + this.props.user);}handleClick() {setTimeout(this.showMessage, 3000);}render() {return <button onClick={this.handleClick}>Follow</button>;} }

    很遺憾,上面的代碼 不能 解決這個問題!記住,導致這個問題的原因,是我們讀取 this.props的時機太晚了,和語法沒有關系。然而,如果我們能夠完全依賴JavaScript的閉包機制,那么就能徹底解決這個問題 。

    我們大多數情況下,會盡量避免使用閉包,因為在閉包的情況下,判斷一個可變的變量值,會變得 有些困難 。但是,在react里,props和state是 不可變的(嚴格來說,我們強烈推薦將props和state作為不可變數據)。props和state的不可變特性,完美解決了使用閉包帶來的問題。

    這意味著,如果在 render方法里,通過閉包來訪問props和state,我們就能確保,在showMessage執行時,訪問到的props和state就是render執行時的那份數據:

    class ProfilePage extends React.Component {render() {// Capture the props!const props = this.props;// Note: we are *inside render*.// These aren't class methods.const showMessage = () => {alert('Followed ' + props.user);};const handleClick = () => {setTimeout(showMessage, 3000);};return <button onClick={handleClick}>Follow</button>;} }

    在render執行時,你成功的捕獲了當時的props 。

    通過這種方式,render方法里的任何代碼,都能訪問到render執行時的props,而不是后面被修改過的值。react不會再偷偷挪動我們的奶酪了。

    像上面這樣,我們可以在render方法里,根據需要添加任何幫助函數,這些函數都能夠正確的訪問到render執行時的props。閉包,這一切的救世主。

    上面的代碼,功能上沒問題,但是看起來有點怪。如果組件邏輯都作為函數定義在render內部,而不是作為類的實例方法,那為什么還要用類呢?

    確實,我們剝離掉類的外衣,剩下的就是一個函數組件:

    function ProfilePage(props) {const showMessage = () => {alert('Followed ' + props.user);};const handleClick = () => {setTimeout(showMessage, 3000);};return (<button onClick={handleClick}>Follow</button>); }

    這個函數組件和上面的類組件一樣,內部函數捕獲了props,react會把props作為函數參數傳進去。和this不同的是,props是不可變的,react不會修改props 。

    如果你在函數參數里,把props結構,代碼看起來會更加清晰:

    function ProfilePage({ user }) {const showMessage = () => {alert('Followed ' + user);};const handleClick = () => {setTimeout(showMessage, 3000);};return (<button onClick={handleClick}>Follow</button>); }

    當父組件傳入不同的props來重新渲染 ProfilePage時,react會再次調用 ProfilePage。但是在這之前,我們點擊關注按鈕的事件處理函數,已經捕獲了上一次render時的props。

    這就是為什么,在 這個例子 的函數組件中,沒有問題。

    可以看到,功能完全是正確的。(再次PS,建議你也關注下 Sunil)

    現在我們理解了,在函數組件和類組件之間的這個差異:

    函數組件會捕獲render內部的狀態

    函數組件配合React Hooks

    在有 Hooks 的情況下,函數組件同樣會捕獲render內部的 state。看下這個例子:

    function MessageThread() {const [message, setMessage] = useState('');const showMessage = () => {alert('You said: ' + message);};const handleSendClick = () => {setTimeout(showMessage, 3000);};const handleMessageChange = (e) => {setMessage(e.target.value);};return (<><input value={message} onChange={handleMessageChange} /><button onClick={handleSendClick}>Send</button></>); }

    (在線demo,點擊這里)

    盡管這是一個很簡陋的消息發送組件,它同樣展示了和前一個例子相同的問題:如果我點擊了發送按鈕,這個組件應該發送的是,我點擊按鈕那一刻輸入的信息。

    OK,我們現在知道,函數組件會默認捕獲props和state。但是,如果你想讀取最新的props、state呢,而不是某一時刻render時捕獲的數據? 甚至我們想在 將來某個時刻讀取舊的props、state 呢?

    在類組件里,我們只需要簡單的讀取 this.props this.state 就能訪問到最新的數據,因為react會修改this。在函數組件里,我們同樣可以擁有一個可變數據,它可以在每次render里共享同一份數據。這就是hooks里的 useRef:

    function MyComponent() {const ref = useRef(null);// You can read or write `ref.current`.// ... }

    但是,你需要自己維護 ref 對應的值。

    函數組件里的 ref 和類組件中的實例屬性 扮演了相同的角色 。你或許已經熟悉 DOM refs,但是hooks里的 ref 更加通用。hooks里的 ref 只是一個容器,你可以往容器里放置任何你想放的東東。

    甚至看起來,類組件里的 this.something 也和hooks里的 something.current 相似,他們確實代表了同一個概念。

    默認情況下,react不會給函數組件里的props、state創造refs。大多數場景下,你也不需要這樣做,這也需要額外的工作來給refs賦值。當然了,你可以手動的實現代碼來跟蹤最新的state:

    function MessageThread() {const [message, setMessage] = useState('');const latestMessage = useRef('');const showMessage = () => {alert('You said: ' + latestMessage.current);};const handleSendClick = () => {setTimeout(showMessage, 3000);};const handleMessageChange = (e) => {setMessage(e.target.value);latestMessage.current = e.target.value;};

    如果我們在 showMessage里讀取 message字段,那么我們會得到我們點擊按鈕時,輸入框的值。但是,如果我們讀取的是 latestMessage.current,我們會得到輸入框里最新的值——甚至我們在點擊發送按鈕后,不斷的輸入新的內容。

    你可以對比 這兩個demo 來看看其中的不同。

    通常來講,你應該避免在render函數中,讀取或修改 refs ,因為 refs 是可變的。我們希望能保證render的結果可預測。但是,如果我們想要獲取某個props或者state的最新值,每次都手動更新refs的值顯得很枯燥 。這種情況下,我們可以使用 useEffect 這個hook:

    function MessageThread() {const [message, setMessage] = useState('');// Keep track of the latest value.const latestMessage = useRef('');useEffect(() => {latestMessage.current = message;});const showMessage = () => {alert('You said: ' + latestMessage.current);};

    (demo 在這里)

    我們在 useEffect 里去更新 ref 的值,這保證只有在DOM更新之后,ref才會被更新。這確保我們對 ref 的修改,不會破壞react中的一些新特性,比如 時間切分和中斷 ,這些特性都依賴 可被中斷的render。

    像上面這樣使用 ref 不會太常見。大多數情況下,我們需要捕獲props和state 。但是,在處理命令式API的情況下,使用 ref 會非常簡便,比如設置定時器,訂閱事件等。記住,你可以使用 ref 來跟蹤任何值——一個prop,一個state,整個props,或者是某個函數。

    使用 ref 在某些性能優化的場景下,同樣適用。比如在使用 useCallback 時。但是,使用useReducer 在大多數場景下是一個 更好的解決方案 。

    總結

    在這篇文章里,我們回顧了類組件中常見的一個問題,以及怎樣使用閉包來解決這個問題。但是,你可能已經經歷過了,如果你嘗試通過指定hooks的依賴項,來優化hooks的性能,那么你很可能會在hooks里,訪問到舊的props或state。這是否意味著閉包會帶來問題呢?我想不是的。

    正如我們上面看到的,在一些不易察覺的場景下,閉包幫助我們解決掉這些微妙的問題。不僅如此,閉包也讓我們在 并行模式 下更加容易寫出沒有bug的代碼。因為閉包捕獲了我們render函數運行時的props和state,使得并行模式成為可能。

    到目前為止,我經歷的所有情況下,訪問到舊的props、state,通常是由于我們錯誤的認為"函數不會變",或者"props始終是一樣的"。實時上不是這樣的,我希望在本文里,能夠幫助你了解到這一點。

    在我們使用函數來開發大部分react組件時,需要更正我們對于 代碼優化 和 哪些狀態會改變 的認知。

    正如 Fredrik說的:

    在使用react hooks過程中,我學習到的最重要規則就是,"任何變量,都可以在任何時間被改變"

    函數同樣遵守這條規則。

    react函數始終會捕獲props、state,最后再強調一下。

    譯注: 有些地方不明白怎么翻譯,有刪減,建議閱讀原文!https://overreacted.io/how-are-function-components-different-from-classes/

    總結

    以上是生活随笔為你收集整理的onclick=两个函数_[译]React函数组件和类组件的差异的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 伊人狼人影院 | 女色婷婷 | 亚洲色图校园春色 | 国产色在线视频 | 中日韩在线播放 | 亚洲色图 欧美 | 可以在线观看av的网站 | 女同久久另类69精品国产 | 香蕉久久久久久久av网站 | 超碰麻豆| 国产一av | 色综合综合色 | 在线视频观看一区二区 | jzzijzzij亚洲成熟少妇 | 日韩精品视频网 | 亚洲精品一区二区三区影院忠贞 | h网站在线播放 | 1024视频污| 日韩h在线 | 91天天操 | 男女啪动最猛动态图 | 天堂va欧美ⅴa亚洲va一国产 | 欧美理论视频 | 少妇高潮一区二区三区99刮毛 | 美女av影院 | 综合成人在线 | 菠萝菠萝蜜网站 | 日韩人妻无码精品综合区 | 中文字幕 自拍偷拍 | 男人操女人逼逼视频 | 午夜看片 | 悠悠色影院 | 亚洲精品乱码久久久久久自慰 | 亚洲五月天综合 | 中文字幕理伦片免费看 | 爱爱的免费视频 | 狼性av懂色av禁果av | 在线免费黄色片 | 制服丝袜av在线 | 2023国产精品 | 亚洲综合伊人久久 | 狠狠躁18三区二区一区传媒剧情 | 丁香在线视频 | 久操青青 | 日韩一片 | 成人av免费 | 亚洲欧美国产高清va在线播放 | 好邻居韩国剧在线观看 | 欧美淫 | 国产黄色av网站 | 日本xxx高清 | 蜜桃av网| 中文字幕无码人妻少妇免费 | 国产又大又黄视频 | 国产国语性生话播放 | 精品人妻一区二区三区麻豆91 | 精品无码国产污污污免费网站 | 午夜日韩在线观看 | 亚洲一区二区在线免费观看 | av在线不卡网 | 久久久久久蜜桃一区二区 | 色综合天天综合综合国产 | a免费在线观看 | 国产成人精品亚洲日本在线观看 | 自拍偷拍中文字幕 | 日韩大片在线免费观看 | 欧美极度另类 | 肉大捧一进一出免费视频 | www.夜夜操| 免费一级片 | 国产一级做a爰片在线看免费 | 九热视频在线观看 | 久久精品夜色噜噜亚洲a∨ 中文字幕av网 | 永久免费视频网站 | 日本国产精品 | 亚洲激情综合网 | 国产精品久久久久久久久久小说 | 中文字幕免费在线观看视频 | 91久久久国产精品 | 性色av蜜臀av浪潮av老女人 | 熊猫成人网 | jlzzjlzz亚洲日本少妇 | 午夜视频在线观看国产 | 中文字幕一区在线观看 | www一级片| 日日操日日爽 | 思思99精品视频在线观看 | 久久国产成人精品国产成人亚洲 | 91九色网| 超碰伊人网 | 91久久精品美女高潮 | 美女的奶胸大爽爽大片 | 欧美日韩国产精品综合 | 国产成人无码精品久在线观看 | av一卡 | 日韩三区四区 | 欧美激情在线狂野欧美精品 | 国产日本一区二区 | 免费网站在线观看黄色 |