最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

SpringCloudalibaba+Vue開發(fā)仿社交小程序-所謂伊人,在水之湄

2023-04-03 23:37 作者:bili_68802470155  | 我要投稿


React 為什么重新渲染

SpringCloudalibaba+Vue開發(fā)仿社交小程序

download:https://www.51xuebc.com/thread-547-1-1.html

更新(重新渲染)是 React 的重要特性 —— 當(dāng)用戶與應(yīng)用交互的時分,React 需求重新渲染、更新 UI,以響應(yīng)用戶的輸入。但是,React 為什么會重新渲染呢?假如不曉得 React 為什么會重新渲染,我們?nèi)绾尾鸥煞乐诡~外的重新渲染呢?

TL; DR
狀態(tài)改動是 React 樹內(nèi)部發(fā)作更新的唯二緣由之一。

這句話是 React 更新的公理,不存在任何例外。本文也將會盤繞解釋這句話展開。為了防止有人抬杠,這句話引入了一些限制定語和關(guān)鍵詞:

名詞解釋
「更新」和「重新渲染」
在 React 中,「更新」和「重新渲染」是關(guān)系嚴(yán)密,但是含義完整不同的兩個詞。下面這句話才干正確表達(dá)這兩個詞的正確含義:

React 的「更新」包含三個階段:渲染(Render),運用 createElement 或 jsx-runtime 產(chǎn)生全新的 React Element 對象、組裝出一顆 React 樹;Reconcilation,React Reconciler 比擬 重生成的 React 樹 和 當(dāng)前的 React 樹,判別如何用最高效的辦法完成「更新」;Commit,操作 Host(如 DOM、Native 等),使新的 UI 呈如今用戶面前。



大局部開發(fā)者會把「更新」和「重新渲染」混為一談,由于在上述三個階段中,只要「渲染」這一階段是開發(fā)者能夠控制的(「Reconcilation」和「Commit」分別由 react-reconciler 和 React Host 控制)。本文接下來的局部中,「重新渲染」一概指代 React 組件在「更新」時的「渲染」階段,而「更新」則一概指代(重新)渲染、Reconcilation 和 Commit 整個過程。

「React 樹」和「React 樹內(nèi)部」
React Tree 自身能夠在恣意時分更新。實踐上,假如你曾經(jīng)經(jīng)過 React 文檔學(xué)習(xí) React,你在「Hello World」一章就曾經(jīng)見過這個 Pattern 了:

const root = ReactDOM.createRoot(document.getElementById('root'));
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
root.render(element);
// 假如你是在 React 18 發(fā)布以前學(xué)習(xí)的 React,你可能會用 ReactDOM.render():
// ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
每秒鐘調(diào)用一次 ReactDOM 提供的 render 使一整顆 React 樹停止了完好的更新。但是絕大局部時分,你不會更新一整顆 React 樹,而是 React 樹內(nèi)的一局部組件(在 React 應(yīng)用中,你只會調(diào)用一次 createRoot().render 或者 hydrateRoot())。

「唯二緣由」
假如你在運用 React class 組件,那么你能夠運用繼承自 React.Component 的 forceUpdate 辦法更新一個組件:

class MyComponent extends React.Component {
handleInput() {
this.forceUpdate();
}
}
因而,我們也能夠把這句話改寫成:假如一顆 React 樹中一切的 class 組件都沒有運用 forceUpdate 辦法,那么狀態(tài)改動是這顆 React Tree 內(nèi)部發(fā)作更新的獨一緣由。

在正文開端之前,先放出一句十分具有迷惑性的話:

誤區(qū) 0:React 組件更新有三個緣由:狀態(tài)改動,prop 改動,Context 改動。
假如你去問一些運用 React 的開發(fā)者「為什么 React 會更新/重新渲染」,大約會得到這個答案。這句話不無道理,但是并不能反響真實的 React 更新機制。

本文只會引見 React 為什么會發(fā)作更新,不會引見如何防止「不用要」的更新(或許我會以這個為話題另外寫一篇文章?)。
狀態(tài)更新和單向數(shù)據(jù)流
讓我們以計數(shù)器為例:

const BigNumber = ({ number }) => (
<div style={{ fontWeight: 700, fontSize: 36 }}>{number}</div>
);
const Counter = () => {
const [count, setCount] = useState(0);
const handleButtonClick = useCallback(() => setCount(count => count + 1), []);
return (
<div>
<BigNumber number={count} />
<button onClick={handleButtonClick}>Increment</button>
</div>
);
};
const App = () => (
<>
<Counter />
<footer>
<a href="https://skk.moe/">Sukka</a>
</footer>
</>
);
在這個例子中,我們聲明了三個組件,根組件 渲染了 ;而 渲染了 。在 組件中,我們聲明了一個組件內(nèi)的狀態(tài) count,當(dāng)點擊按鈕時會改動狀態(tài) count、使其遞增。

當(dāng)我們點擊按鈕的時分,setCount 被調(diào)用、count 狀態(tài)發(fā)作改動,React 更新了 組件。而當(dāng) React 更新一個組件時,也會更新這個組件下的一切子組件(至于為什么,很快就會講的)。因而 組件更新時,子組件 也會更新。

如今讓我們先厘清一個最簡單的誤區(qū):

誤區(qū) 1:當(dāng)一個狀態(tài)發(fā)作改動時,整顆 React 樹都會更新。
有少數(shù)運用 React 的開發(fā)者會置信這一點(還好不是大多數(shù)?。嵺`上,當(dāng)狀態(tài)發(fā)作改動的時分,React 只會更新「具有這個狀態(tài)」的組件,和這個組件的一切子組件。

為什么父組件(在這個例子中, 是 的父組件)沒有發(fā)作更新呢?由于 React 的主要任務(wù)就是堅持 React 內(nèi)的狀態(tài)和 React 渲染的 UI 的同步。React 更新,就是找出如何改動 UI,使其和新的狀態(tài)同步。而在 React 中,數(shù)據(jù)是自上而下單向傳送的(單向數(shù)據(jù)流,The Data Flows Down)。在這個例子中, 組件的狀態(tài) count 向下流向了 組件的 prop number,但是不可能向上流向了 組件。因而,count 狀態(tài)改動, 組件并不需求更新。

當(dāng) count 狀態(tài)改動時, 組件及其子組件 都發(fā)作了更新。而 組件更新時,運用了 prop number 的新的值停止渲染。那么 組件更新的緣由是由于 prop number 的改動嗎?

不,和 props 完整沒有關(guān)系

誤區(qū) 2:React 組件更新的其中一個緣由是它的 prop 發(fā)作了改動。
如今讓我們修正一下上面那個例子:

import BigNumber from './big-number';
const SomeDecoration = () => <div>Hooray!</div>
const Counter = () => {
const [count, setCount] = useState(0);
const handleButtonClick = useCallback(() => setCount(count => count + 1), []);
return (
<div>
<BigNumber number={count} />
<button onClick={handleButtonClick}>Increment</button>
<SomeDecoration />
</div>
);
};
const App = () => (
<>
<Counter />
<footer>
<a href="https://skk.moe/">Sukka</a>
</footer>
</>
);
組件不承受任何 prop、不運用其父組件 的 count 狀態(tài),但是當(dāng) count 狀態(tài)發(fā)作改動時, 組件依然發(fā)作了更新。當(dāng)一個組件更新時,React 會更新 一切的子組件,不論這個子組件能否承受一個 prop:React 并不能百分之百肯定 組件能否直接/間接地依賴了 count 狀態(tài)。

理想中,每一個 React 組件都應(yīng)該是一個 純函數(shù) —— 一個「純」的 React 組件,當(dāng)輸入相同的 props 時,總是會渲染相同的 UI。但是理想是骨感的,我們十分容易寫出一個「不純」的 React 組件:

const CurrentTime = () =>
Last rendered at {new Date().toString()}

包含了狀態(tài)(運用了 useState)的組件也不是純組件:即便 prop 不改動,組件也會由于狀態(tài)不同而渲染出不同的 UI。

有的時分,你很難判別一個組件能否是純組件。你可能會將一個 Ref 作為 prop 傳送給一個組件(forwardRef,useImperativeHandle,諸如此類的 case)。Ref 自身是 Reference Stable 的、React 并不能曉得 Ref 中的值能否改動。

React 的目的是展現(xiàn)最新、維持分歧的 UI。為了防止向用戶展現(xiàn)過時的 UI,當(dāng)父組件更新時,React 會更新一切子組件,即便子組件不承受任何 prop。props 和組件更新沒有任何關(guān)系。

純組件和 memo
你大約很熟習(xí)(或者至少聽說過)React.memo、shouldComponentUpdate 或者 React.PureComponent,這些工具允許我們「疏忽更新」:

const SomeDecoration = memo(() => <div>Hooray!</div>);
當(dāng)我們將 組件的聲明包裹在 memo 中時,我們實踐上做的是通知 React「嘿!我覺得這是個純組件,只需它的 prop 不改動,我們就別更新它」。

如今,讓我們把 和 都包裹在 memo 中,看看會發(fā)作什么:

const BigNumber = memo(({ number }) => (
<div style={{ fontWeight: 700, fontSize: 36 }}>{number}</div>
));
const SomeDecoration = memo(() => <div>Hooray!</div>);
const Counter = () => {
const [count, setCount] = useState(0);
const handleButtonClick = useCallback(() => setCount(count => count + 1), []);
return (
<div>
<BigNumber number={count} />
<button onClick={handleButtonClick}>Increment</button>
<SomeDecoration />
</div>
);
};
const App = () => (
<>
<Counter />
<footer>
<a href="https://skk.moe/">Sukka</a>
</footer>
</>
);
如今,當(dāng) count 狀態(tài)更新后,React 會更新 組件及其一切子組件, 和 。由于 承受一個 prop number,而 number 的值發(fā)作了改動,因而 會更新。但是 的 prop 沒有發(fā)作改動(由于不承受任何 prop),所以 React 跳過了 的更新。

于是你想,為什么 React 不默許一切組件都是純組件呢?為什么 React 不 memo 一切組件呢?事實上,React 組件更新的開支沒有想象中的那么大。以 組件為例,它只需求渲染一個

。
假如一個組件承受很多復(fù)雜的 prop,有可能渲染這個組件并比照 Virtual DOM 的性能開支以至小于等于淺比擬一切 prop 的開支。絕大局部時分,React 是足夠快的。因而,只要當(dāng)一個 純組件 有大量純的子組件、或者這個 純組件 內(nèi)部有很多復(fù)雜計算時,我們才需求將其包裹在 memo 中。

當(dāng)一個包裹在 memo 中的組件運用了 useState、useReducer 或者 useContext,當(dāng)這個組件內(nèi)的狀態(tài)發(fā)作改動時,這個組件依然會更新。
另外一個 React 默許不 memo 一切組件的緣由是:讓 React 在 Runtime 中判別子組件的全部依賴、以跳過子組件的不用要更新,是十分艱難、十分不理想的。計算子組件依賴的最好機遇是編譯期間。關(guān)于這個 idea 的更多細(xì)節(jié),能夠看看黃玄在 React Conf 2021 上的演講 React without memo。

讓我們談?wù)?Context
誤區(qū) 3:React 組件更新的其中一個緣由是 Context.Provider 的 value 發(fā)作了更新。
假如說,當(dāng)一個組件由于狀態(tài)改動而更新時,其一切子組件都要隨之更新。那么當(dāng)我們經(jīng)過 Context 傳送的狀態(tài)發(fā)作改動時,訂閱了這個 Context 的一切子組件都要更新也是毫不不測的了。

關(guān)于純組件來說,Context 能夠視為一個「躲藏的」、或者「內(nèi)部的」prop:

const User = memo(() => {
const user = useContext(UserContext);
if (!user) {
return 'Hello, new comer!';
}
return `Hello, ${user.name}!`;
})
在上面的例子中, 組件是一個不承受任何 prop、不運用 useState、也沒有任何反作用的純組件。但是, 組件依賴 UserContext。當(dāng) UserContext 保管的狀態(tài)發(fā)作改動時, 組件也會更新。

眾所周知,當(dāng) Context 的 value 發(fā)作改動的時分,一切 <Context.Provider /> 的子組件都會更新。那么為什么即便不依賴 Context 的子組件也會更新呢?Context 自身并不是一個狀態(tài)管理工具,只是一種狀態(tài)傳送工具。Context 的 value 發(fā)作改動的基本緣由還是狀態(tài)的改動:

const CountContext = createContext(0);
const BigNumber = memo(() => {
const number = useContext(CounterContext);
return (
<div style={{ fontWeight: 700, fontSize: 36 }}>{number}</div>
)
});
const Counter = () => {
const [count, setCount] = useState(0);
const handleButtonClick = useCallback(() => setCount(count => count + 1), []);
return (
<div>
<CountContext.Provider value={count}>
<BigNumber number={count} />
</CountContext.Provider>
<SomeDecoration />
<button onClick={handleButtonClick}>Increment</button>
</div>
);
};
正如上面的例子,CountContext 發(fā)作改動的緣由,是 組件的 count 狀態(tài)發(fā)作了改動;發(fā)作更新的,也不只僅是 CountContext 的消費組件(及其子組件),還包括 一切的子組件。

代碼部署后可能存在的BUG沒法實時曉得,事后為理解決這些BUG,花了大量的時間停止log 調(diào)試,


SpringCloudalibaba+Vue開發(fā)仿社交小程序-所謂伊人,在水之湄的評論 (共 條)

分享到微博請遵守國家法律
左权县| 揭西县| 延津县| 涪陵区| 石河子市| 嘉善县| 贞丰县| 高淳县| 临颍县| 鹰潭市| 铜山县| 从江县| 明光市| 万盛区| 太康县| 临猗县| 定边县| 黄浦区| 莆田市| 余江县| 台南市| 砚山县| 商都县| 银川市| 化州市| 江城| 陇南市| 潞西市| 香港| 沭阳县| 监利县| 麟游县| 江门市| 山阴县| 额敏县| 营山县| 花莲市| 吉木乃县| 吉木萨尔县| 资源县| 白城市|