React函數(shù)組件與類(lèi)的區(qū)別有哪些
首先我們要知道的是,項(xiàng)目性能能主要取決于代碼的作用,而不是選擇函數(shù)式還是類(lèi)組件。盡管優(yōu)化策略各有略微不同,但它們之間的性能差異可以忽略不計(jì)。
一、函數(shù)式組件捕獲了渲染所用的值
首先我們來(lái)看下面這個(gè)組件:
function App(props) {
const showMessage = () => {
alert('Hello' + props.user);
};
const handleClick = () => {
setTimeout(showMessage, 3000);
};
return (
<button onClick={handleClick}>Say</button>
);
}它渲染了一個(gè)利用來(lái)模擬網(wǎng)絡(luò)請(qǐng)求,然后顯示一個(gè)確認(rèn)警告的按鈕。例如,如果是傳遞進(jìn)來(lái)的 props.user 是 jie,那么三秒后就會(huì)彈出 Hello jie。
那么我們用類(lèi)應(yīng)該怎么寫(xiě)這個(gè)組件呢?一個(gè)簡(jiǎn)單的重構(gòu)可能就象這樣:
class App extends React.Component {
showMessage = () => {
alert('Hello' + this.props.user);
};
handleClick = () => {
setTimeout(this.showMessage, 3000);
};
render() {
return <button onClick={this.handleClick}>Say</button>;
}
}我們通常做代碼重構(gòu)的時(shí)候都認(rèn)為他們兩個(gè)是等效的,但是事實(shí)真的如此嗎,我們很少注意到它們之間的含義。
下面我們新建一個(gè) react 項(xiàng)目,在 src下新建兩個(gè)組件,一個(gè) classComponent 組件,一個(gè)是 functionComponent 組件。代碼就是上面我們寫(xiě)的這兩個(gè)組件,只不過(guò)內(nèi)容稍有區(qū)別:
classComponent:
import React from 'react';
class ProfilePage extends React.Component {
showMessage = () => {
alert('你選擇了 ' + this.props.user);
};
handleClick = () => {
setTimeout(this.showMessage, 3000);
};
render() {
return <button onClick={this.handleClick}>選擇</button>;
}
}
export default ProfilePage;functionComponent:
import React from 'react';
function ProfilePage(props) {
const showMessage = () => {
alert('你選擇了 ' + props.user);
};
const handleClick = () => {
setTimeout(showMessage, 3000);
};
return (
<button onClick={handleClick}>選擇</button>
);
}
export default ProfilePage;在 app.js 中我們將這兩個(gè)組件引入:
import React from "react";
import ReactDOM from "react-dom";
import ProfilePageFunction from './functionComponent';
import ProfilePageClass from './classComponent';
export default class App extends React.Component {
state = {
user: '小杰',
};
render() {
return (
<>
<label>
<b>選擇你想要拜訪的朋友</b>
<select
value={this.state.user}
onChange={e => this.setState({ user: e.target.value })}
>
<option value="小杰">小杰</option>
<option value="小尚">小尚</option>
<option value="小寧">小寧</option>
</select>
</label>
<h1>歡迎來(lái)到 {this.state.user}的 家!</h1>
<p>
<ProfilePageFunction user={this.state.user} />
<b> (這是來(lái)自函數(shù)式組件的)</b>
</p>
<p>
<ProfilePageClass user={this.state.user} />
<b> (這是來(lái)自類(lèi)組件的)</b>
</p>
</>
)
}
}運(yùn)行項(xiàng)目,科研看到這樣的界面:

當(dāng)我們單擊上面的按鈕時(shí),執(zhí)行的就是函數(shù)式組件,點(diǎn)擊下面的按鈕時(shí),執(zhí)行的就是類(lèi)。如果按照我們以往的思路,他們二者都會(huì)有相同的結(jié)果,但事實(shí)真的如此嗎?
我們按照下面的順序執(zhí)行:
1. 點(diǎn)擊函數(shù)式組件按鈕
2. 在點(diǎn)擊后立刻切換想要拜訪的朋友
函數(shù)式組件的執(zhí)行結(jié)果如下:

頁(yè)面彈出的還是我們當(dāng)時(shí)選擇的值
同樣的操作我們?cè)僭囈幌骂?lèi)組件:

現(xiàn)在頁(yè)面彈出的就是我們實(shí)時(shí)更改的值了。
在這個(gè)例子中,第一個(gè)行為是正確的。因?yàn)樽铋_(kāi)始我選擇要拜訪小杰點(diǎn)擊了確定發(fā)出了命令,然后我再切換到小尚,但是我并沒(méi)有點(diǎn)擊確定,我的組件不應(yīng)該混淆我要拜訪的人。在這里,類(lèi)組件的實(shí)現(xiàn)很明顯是錯(cuò)誤的。
所以為什么我們的例子中類(lèi)組件會(huì)有這樣的表現(xiàn)?
讓我們來(lái)仔細(xì)看看我們類(lèi)組件中的方法:showMessage
showMessage = () => {
alert('你選擇了 ' + this.props.user);
};這個(gè)類(lèi)方法從中讀取數(shù)據(jù)。在 React 中 Props 是不可變的,所以他們永遠(yuǎn)不會(huì)改變。然而,this是,而且永遠(yuǎn)是,可變的。
事實(shí)上,這就是類(lèi)組件存在的意義。React本身會(huì)隨著時(shí)間的推移而改變,以便你可以在渲染方法以及生命周期方法中得到最新的實(shí)例。所以如果在請(qǐng)求已經(jīng)發(fā)出的情況下我們的組件進(jìn)行了重新渲染,將會(huì)改變。
我們的組件屬于一個(gè)擁有特定 props 和 state 的特定渲染。
然而,調(diào)用一個(gè)回調(diào)函數(shù)讀取 的 timeout 會(huì)打斷這種關(guān)聯(lián)。我們的回調(diào)并沒(méi)有與任何一個(gè)特定的渲染綁定在一起,所以它失去了正確的 props。
二、閉包讓類(lèi)組件成為擁有特定props和state的渲染
我們想要以某種方式“修復(fù)”擁有正確 props 的渲染與讀取這些 props 的回調(diào)之間的聯(lián)系。它們?cè)陬?lèi)的某個(gè)地方被弄丟了。
一種方法是在調(diào)用事件之前讀取,然后將他們顯式地傳遞到timeout回調(diào)函數(shù)中去:
import React from 'react';
class ProfilePage extends React.Component {
showMessage = (user) => {
alert('你選擇了 ' + user);
};
handleClick = () => {
const {user} = this.props;
setTimeout(() => this.showMessage(user), 3000);
};
render() {
return <button onClick={this.handleClick}>確定</button>;
}
}
export default ProfilePage;這種方法會(huì)起作用。然而,這種方法使得代碼明顯變得更加冗長(zhǎng),并且隨著時(shí)間推移容易出錯(cuò)。如果我們需要的不止是一個(gè)props怎么辦?如果我們還需要訪問(wèn)state怎么辦?
然而,如果我們能利用JavaScript閉包的話(huà)問(wèn)題將迎刃而解。
通常來(lái)說(shuō)我們會(huì)避免使用閉包,但是在React中,props和state是不可變的,這就消除了閉包的一個(gè)主要缺陷。
這就意味著如果你在一次特定的渲染中捕獲那一次渲染所用的props或者state,你會(huì)發(fā)現(xiàn)他們總是會(huì)保持一致,就如同你的預(yù)期那樣。
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>;
}
}你在渲染的時(shí)候就已經(jīng)“捕獲”了props。這樣,在它內(nèi)部的任何代碼(包括)都保證可以得到這一次特定渲染所使用的props。上面的例子是正確的,但是看起來(lái)很奇怪。如果你在方法中定義各種函數(shù),而不是使用class的方法,那么使用類(lèi)的意義在哪里?
所以這個(gè)時(shí)候我們就明白了函數(shù)式組件和類(lèi)組件的區(qū)別:
function ProfilePage({ user }) {
const showMessage = () => {
alert('Followed ' + user);
};
const handleClick = () => {
setTimeout(showMessage, 3000);
};
return (
<button onClick={handleClick}>Follow</button>
);
}當(dāng)父組件使用不同的props來(lái)渲染時(shí),React會(huì)再次調(diào)用函數(shù)。但是我們點(diǎn)擊的事件處理函數(shù),"屬于"具有自己的值的上一次渲染,并且回調(diào)函數(shù)也能讀取到這個(gè)值。它們都保持完好無(wú)損。
三、區(qū)分useState與useRef的使用
使用Hooks,同樣的原則也適用于狀態(tài)。看這個(gè)例子:
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>
</>
);
}如果我發(fā)送一條特定的消息,組件不應(yīng)該對(duì)實(shí)際發(fā)送的是哪條消息感到困惑。這個(gè)函數(shù)組件的變量捕獲了我們?cè)跒g覽器中執(zhí)行單擊處理函數(shù)的那一次渲染。所以當(dāng)我點(diǎn)擊“發(fā)送”時(shí)那一刻輸入框中的內(nèi)容就會(huì)被設(shè)置為彈出的值。
因此我們知道,在默認(rèn)情況下React中的函數(shù)會(huì)捕獲props和state。但是如果我們想要讀取并不屬于這一次特定渲染的,最新的props和state呢?
在函數(shù)式組件中,你也可以擁有一個(gè)在所有的組件渲染幀中共享的可變變量。它被成為“ref”:
function MyComponent() {
const ref = useRef(null);
// 你可以通過(guò) ref.current 來(lái)獲取保存的值.
// ...
}在很多情況下,你并不需要它們,并且分配它們將是一種浪費(fèi)。但是,如果你愿意,你可以這樣手動(dòng)地來(lái)追蹤這些值:
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;
};如果我們?cè)?state 中讀取,我們將得到在我們按下發(fā)送按鈕那一刻的信息。但是當(dāng)我們通過(guò) ref 讀取時(shí),我們將得到最新的值,即使我們?cè)诎聪掳l(fā)送按鈕后繼續(xù)輸入。
通常情況下,你應(yīng)該避免在渲染期間讀取或者設(shè)置refs,因?yàn)樗鼈兪强勺兊?。我們希望保持渲染的可預(yù)測(cè)性。然而,如果我們想要特定props或者state的最新值,那么手動(dòng)更新ref會(huì)有些煩人。我們可以通過(guò)使用一個(gè)effect來(lái)自動(dòng)化實(shí)現(xiàn)它:
function MessageThread() {
const [message, setMessage] = useState('');
// 保持追蹤最新的值。
const latestMessage = useRef('');
useEffect(() => {
latestMessage.current = message;
});
const showMessage = () => {
alert('You said: ' + latestMessage.current);
};正如我們上面看到的,閉包實(shí)際上幫我們解決了很難注意到的細(xì)微問(wèn)題。同樣,它們也使得在并發(fā)模式下能更輕松地編寫(xiě)能夠正確運(yùn)行的代碼。這是可行的,因?yàn)榻M件內(nèi)部的邏輯在渲染它時(shí)捕獲并包含了正確的props和state。
React函數(shù)總是捕獲他們的值 —— 現(xiàn)在我們也知道這是為什么了。
到此這篇關(guān)于React函數(shù)組件與類(lèi)的區(qū)別有哪些的文章就介紹到這了,更多相關(guān)React函數(shù)組件與類(lèi)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Remix?路由模塊輸出對(duì)象handle函數(shù)
這篇文章主要為大家介紹了Remix?路由模塊輸出對(duì)象handle函數(shù)使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
Vite+React+TypeScript手?jǐn)]TodoList的項(xiàng)目實(shí)踐
本文主要介紹了Vite+React+TypeScript手?jǐn)]TodoList的項(xiàng)目實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05
React中實(shí)現(xiàn)父組件調(diào)用子組件的三種方法
在React中,組件之間的通信是一個(gè)常見(jiàn)的需求,有時(shí),我們需要從父組件調(diào)用子組件的方法,這可以通過(guò)幾種不同的方式實(shí)現(xiàn),需要的朋友可以參考下2024-04-04
react+react-beautiful-dnd實(shí)現(xiàn)代辦事項(xiàng)思路詳解
這篇文章主要介紹了react+react-beautiful-dnd實(shí)現(xiàn)代辦事項(xiàng),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06
React Hook 父子組件相互調(diào)用函數(shù)方式
這篇文章主要介紹了React Hook 父子組件相互調(diào)用函數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09

