React 組件生命周期

来源:https://www.cnblogs.com/hubert-style/archive/2023/12/08/17888425.html
-Advertisement-
Play Games

求上進的人,不要總想著靠誰,人都是自私的,自己才是最靠得住的人。 React 中生命周期劃時代幾個節點,React 16.2 之前處於老的生命周期,之後提出了新的生命周期。而函數式組件在 React 16.8 之前是沒有狀態和生命周期的,在 React 16.8 版本通過引入 Hooks 使得函數式 ...


求上進的人,不要總想著靠誰,人都是自私的,自己才是最靠得住的人。

React 中生命周期劃時代幾個節點,React 16.2 之前處於老的生命周期,之後提出了新的生命周期。而函數式組件在 React 16.8 之前是沒有狀態和生命周期的,在 React 16.8 版本通過引入 Hooks 使得函數式組件也能有狀態和生命周期了。

1. 初始化階段

1.1 componentWillMount:

組件即將掛載,初始化數據作用,即 render 之前最後一次修改狀態的機會。

// 組件即將掛載
componentWillMount() {
  // 初始化數據作用
  console.log("componentWillMount")
}

/* 在 16.2 之後版本使用會出現以下警告 ⚠️⚠️⚠️
  react-dom.development.js:86 Warning: componentWillMount has been renamed, and is not recommended for use. See https://reactjs.org/link/unsafe-component-lifecycles for details.

  * Move code with side effects to componentDidMount, and set initial state in the constructor.
  * Rename componentWillMount to UNSAFE_componentWillMount to suppress this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run `npx react-codemod rename-unsafe-lifecycles` in your project source folder.

  Please update the following components: App
  */

// 組件即將掛載 - 強制去掉警告,UNSAFE 提示開發者這是一個不安全的生命周期方法。
UNSAFE_componentWillMount() {
  // 初始化數據作用
  console.log("componentWillMount")
}

componentWillMount 在16.2 之後官方不推薦使用了,這是因為 16.2 的時候 React 發生了一個改變,推出了幾個新的生命周期,老的生命周期方法被替代掉了,不推薦使用。 那麼為什麼 React 要推出新的生命周期呢?

在 React 16.2 中通過對 diff 演算法的更新,更加優化它的性能。提出了一個 Fiber 技術(纖維、分片、切片,比線程更小的一種概念)。因為我們傳統的 React 它在創建、更新狀態之後會創建新的虛擬 Dom 樹,會對比老的虛擬 Dom 樹,這個過程是同步的,如果數據量比較小還好。如果這個數據量非常多的情況下即組件非常多的情況下(例如:幾百個組件),這個時候更新操作,會導致我們瀏覽器假死、卡頓,這個時候點什麼都沒有反應,因為它忙著新老虛擬 Dom 的對比,就是它在對比兩個超級大的對象,裡面包含了很多小對象,這時瀏覽器無法處理其它事件,所以導致卡頓影響體驗。

所以這個東西就是一個邊緣化的問題,你的組件達到這樣一個程度,它真的會出現假死的情況。所以 React Fiber 技術就是來優化了虛擬 Dom 的 diff 演算法,它把創建 Dom 和組件渲染整個過程拆分成了無數個小的分片任務來執行。可以認為這個任務無比的碎片化,這個時候如果有優先順序較高的任務就先執行優先順序較高的任務。當優先順序較低的任務在執行時候突然來了優先順序較高的任務,這個時候會打斷正在執行的低優先順序任務,先執行優先順序高的任務。所謂的低優先順序任務就是 componentWillMount 中去找哪些節點將要去掛載到頁面中,而高優先順序任務就是 render 正在渲染,didMount 掛載完成。這個時候我們低優先順序的任務(找出那些需要更新的 Dom)這個過程是會被打斷的,而我們更新 Dom 這個過程是不能被打斷的,只能一鼓作氣做完的,而 willMount 很不幸它是處在這個要找出來那些 Dom 是需要被更新的。所以這個過程是可以被打斷的,所以可以認為 willMount 在忙著找出那些狀態需要更新。因為接下來在 render 中就要開始更新了,didMount 就更新完成了。這個時候 willMount 找是處於低優先順序的,而這個時候 render 正在更新,因為碎片化任務,他可能還不是同步的。即某個組件可能處在在找那個狀態需要更新,那個 Dom 需要更新,而那邊組件已經到了 render 渲染部分了,這個時候就吧低優先順序的任務給砍掉了。砍掉怎麼辦,會保存嗎?不會。只能下次再來一遍,再來找那個節點需要更新。所以這個生命周期就可能會觸發多次這樣一個問題(失去了唯一性),所以這是一個有隱患的生命周期方法,所以這裡不推薦使用。

1.2 render

組件正式掛載渲染,只能訪問 this.props 和 this.state,不允許修改狀態和 Dom 輸出。

1.3 componentDidMount

組件掛載完成,成功 render 並渲染完成真實 DOM 之後觸發,可以訪問、修改 Dom。

componentDidMount() {
  // 數據請求axios
  // 訂閱函數調用
  // setInterval
  // 基於創建的完的dom進行初始化時候,例如 BetterScroll 使用
  console.log("componentDidMount")
}

2. 運行中階段

2.1 componentWillUpdate

組件即將更新,不能修改屬性和狀態,會造成死迴圈。非安全被棄用,同 componentWillMount。

// 組件即將更新
componentWillUpdate() {
  console.log("componentWillUpdate")
}

/* 在 16.2 之後版本使用會出現以下警告 ⚠️⚠️⚠️
  react-dom.development.js:86 Warning: componentWillUpdate has been renamed, and is not recommended for use. See https://reactjs.org/link/unsafe-component-lifecycles for details.

* Move data fetching code or side effects to componentDidUpdate.
* Rename componentWillUpdate to UNSAFE_componentWillUpdate to suppress this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run `npx react-codemod rename-unsafe-lifecycles` in your project source folder.

Please update the following components: App
  */

// 組件即將更新 - 強制去掉警告,UNSAFE 提示開發者這是一個不安全的生命周期方法。
 UNSAFE_componentWillUpdate() {
  console.log("componentWillUpdate")
}

2.2 render

組件正式掛載渲染,只能訪問 this.props 和 this.state,不允許修改狀態和 Dom 輸出。

2.3 componentDidUpdate

組件更新完成,成功 render 並渲染完成真實 DOM 之後觸發,可以訪問、修改 Dom。

// 組件更新完成 - 接收兩個行參,老的屬性、老的狀態
componentDidUpdate(prevProps, prevState) {
  console.log(prevState)
  console.log("componentDidUpdate")
}

2.4 shouldComponentUpdate

scu 控制組件是否應該更新,即是否執行 render 函數。

// 組件是否應該更新?- 接受兩個行參,新的屬性、新的狀態
shouldComponentUpdate(nextProps, nextState) {
  if (JSON.stringify(this.state) !== JSON.stringify(nextState)) {
    return true
  } else {
    return false
  }
}

2.5 componentWillReceiveProps

父組件修改屬性觸發,非安全被棄用,同 componentWillMount。處在 diff 中第一個階段,找到哪些需要更新的 Dom。例如:如果父組件連續多次修改屬性傳遞將觸發多次 ajax 請求等。

// 父組件修改屬性觸發,應用在子組件中才有意義
componentWillReceiveProps(nextProps) {
  // 最先獲得父組件傳來的屬性,可以利用屬性進行ajax或者邏輯處理
  // 把屬性轉換為孩子的自己的狀態等
}

3. 銷毀階段

3.1 componentWillUnmount

在刪除組件之前進行清理操作,比如計時器和事件監聽器。

4. 新生命周期

4.1 getDerivedStateFromProps

getDerivedStateFromProps 第一次的初始化組件以及後續的更新過程中(包括自身狀態更新以及父傳子),返回一個對象作為新的 state,返回 null 則說明不需要在這裡更新 state。

在初始化中代替 componentWillMount 。在父傳子中能代替 componentWillReceiveProps。

這裡不能做非同步操作,因為這裡 return 是立即返回的。

static getDerivedStateFromProps(nextProps, nextState) {
  console.log("getDerivedStateFromProps")
  return {

  }
}

4.2 getSnapshotBeforeUpdate

getSnapshotBeforeUpdate 取代了 componentDidUpdate,觸發時間為 update 發生的時候,在 render之後 Dom 渲染之前返回一個值,作為 componentDidUpdate 的第三個參數。

import React, { Component } from 'react'

export default class App extends Component {
  state = {

  }

  // getDerivedStateFromProps 第一次的初始化組件以及後續的更新過程中(包括自身狀態更新以及父傳子),返回一個對象作為新的 state,返回 null 則說明不需要在這裡更新 state
  // 這裡不能做非同步操作,因為這裡 return 是立即返回的
  static getDerivedStateFromProps(nextProps, nextState) {
    console.log("getDerivedStateFromProps")
    return {

    }
  }

  getSnapshotBeforeUpdate() {
    return 100
  }

  componentDidUpdate(prevProps, prevState, value) {
    console.log(value)
  } 

  render() {
    return (
      <div>
        <button onClick={()=>{
          this.setState({

          })
        }}>修改</button>
      </div>
    )
  }
}

5. React 中性能優化

5.1 shouldComponentUpdate

React 手動優化,控制組件自身或者子組件是否需要更新,尤其在子組件非常多的情況下,需要進行優化。

5.2 PureComponent

React 自動優化,PureComponent 會幫你比較新 props 跟舊的 props,新的 state 和老的 state(值相等,或者對象含有相同的屬性、且屬性值相等),決定 shouldComponentUpdate 返回 true 或者 false,從而決定要不要呼叫 render function。

註意:如果你的 state 或 props『永遠都會變』,那 PureComponent 並不會比較快,因為 shallowEqual 也需要花時間,比如倒計時功能,這就不適合使用 Pure Component 了。

5.3 緩存技術

React.Component 是使用 ES6 classes 方式定義 React 組件的基類:

class Greeting extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

PureComponent 和 memo 僅作為性能優化的方式而存在。但請不要依賴它來“阻止”渲染,因為這會產生 bug。PureComponnet 和 memo 都是通過對 props 值的淺比較來決定該組件是否需要更新的。

2.1 PureComponent (類組件)

React.PureComponent 與 React.Component 很相似。兩者的區別在於 React.Component 並未實現 shouldComponentUpdate(),而 React.PureComponent 中以淺層對比 props 和 state 的方式來實現了該函數。
如果賦予 React 組件相同的 props 和 state,render() 函數會渲染相同的內容,那麼在某些情況下使用 React.PureComponent 可提高性能。

2.2 memo(函數式組件)

函數組件緩存 memo,為啥起 memo 這個名字?在電腦領城,記記化是一種主要用來提高電腦程式速度的優化技術方案。它將開銷較大的函數調用的返回結果存儲起來,當同樣的輸入再次發生時,則這回緩存好的數據,以此提升運算效率。

React.memo 為高階組件。它與 React.PureComponent 非常相似,但只適用於函數組件,而不適用 class 組件。

const MyComponent = function MyComponent(props) {
  /* 使用 props 渲染 */
};
export default React.memo(MyComponent)

如果你的函數組件在給定相同 props 的情況下渲染相同的結果,那麼你可以通過將其包裝在 React.memo 中調用,以此通過記憶組件渲染結果的方式來提高組件的性能表現。這意味著在這種情況下,React 將跳過渲染組件的操作並直接復用最近一次渲染的結果,即組件僅在它的 props 發生改變的時候進行重新渲染。通常來說,在組件樹中 React 組件,只要有變化就會走一遍渲染流程。但是 React.memo(),我們可以僅僅讓某些組件進行渲染。

React.memo 僅檢查 props 變更。如果函數組件被 React.memo 包裹,且其實現中擁有 useState 或 useContext 的 Hook,當 context 發生變化時,它仍會重新渲染。

預設情況下其只會對複雜對象做淺層對比,如果你想要控制對比過程,那麼請將自定義的比較函數通過第二個參數傳入來實現。

function MyComponent(props) {
  /* 使用 props 渲染 */
}
function areEqual(prevProps, nextProps) {
  
}
export default React.memo(MyComponent, areEqual);

示例:

// 子組件代碼:
import React, { memo } from 'react';
const Child = ()=>{
	console.log("2. 子組件渲染了")
	return (<div>子組件</div>)
}
export default Child

// 父組件代碼:
import React, { memo } from 'react';
import Child from './Child.jsx'
const Father = ()=>{
	const [name,setName]=React.useState('');
	console.log("1. 父組件渲染了")
	return (<div>
		/* 在input框中輸入內容,會走setName導致App組件重新渲染,但是子組件Child也會進行渲染。 */
		父組件:<input type="text" value={name} onChange={ev=>setName(ev.target.value)} />
		<Child />
	</div>)
}
// 子組件代碼:
import React, { memo } from 'react';
const Child = ()=>{
	console.log("2. 子組件渲染了")
	return (<div>子組件</div>)
}
export default memo(Child)
// 父組件代碼:
import React, { memo } from 'react';
import Child from './Child.jsx'
const Father = ()=>{
	const [name,setName]=React.useState('');
	console.log("1. 父組件渲染了")
	return (<div>
		/* 解決:子組件使用memo包起來 */
		父組件:<input type="text" value={name} onChange={ev=>setName(ev.target.value)} />
		<Child />
	</div>)
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 本文要說的這種開發模式,這種模式並不是只有blazor支持,js中有一樣的方案next.js nuxt.js;blazor還有很多其它內容,本文近關註漸進式開發模式。 是的,前後端是主流,不過以下情況也許前後端分離並不是最好的選擇: 小公司,人員不多,利潤不高,創業階段能省則省 個人開發者,接 ...
  • 使用Aspirate可以將Aspire程式部署到Kubernetes 集群 工具安裝 dotnet tool install -g aspirate --prerelease 註意:Aspirate 正在開發中,該軟體包將作為預覽版進行版本控制,--prelease 選項將獲得最新的預覽版。 容器註 ...
  • 本篇將分享Prometheus+Grafana的監控平臺搭建,並監控之前文章所搭建的主機&服務,分享日常使用的一些使用經驗本篇將配置常用服務的監控與面板配置:包括 MySQL,MongoDB,CLickHouse,Redis,RabbitMQ,Linux,Windows,Nginx,站點訪問監控,已... ...
  • 當使用Autofac處理一個介面有多個實現的情況時,通常會使用鍵(key)進行區分或者通過IIndex索引註入,也可以通過IEnumerable集合獲取所有實例,以下是一個具體的例子,演示如何在Autofac中註冊多個實現,並通過構造函數註入獲取指定實現。 首先,確保你已經安裝了Autofac Nu ...
  • tmux教程 功能 分屏:可以在一個開發框里分屏 允許terminal在連接斷開之後可以繼續運行,讓進程不會因為斷開連接而中斷 結構 // 一個tmux可以包含多個session,一個session可以包含多個window,一個window可以包含多個pane。 tmux: session 0: w ...
  • 本文分享自華為雲社區《GaussDB資料庫SQL系列-層次遞歸查詢》,作者: Gauss松鼠會小助手2。 一、前言 層次遞歸查詢是一種常見的SQL查詢方式,特別是在一些層次化的數據存儲結構中經常用到。本文主要以GaussDB資料庫為實驗平臺,為大家講解其使用方法。 二、GuassDB資料庫層次遞歸查 ...
  • 在我們應用中的使用場景來看,簡單來說通常會看中了clickhouse在處理大批量數據的寫入和讀取分析方面的性能,MySQL會主要負責一些基於模型進行指標二次加工的高頻查詢及複雜join的查詢。 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 大家好,今天聊一下在做uniapp多端適配項目,需要用到自定義導航時,如何解決狀態欄塌陷及導航欄安全區域多端適配問題,下文只針對H5、APP、微信小程式三端進行適配,通過封裝一個通用高階組件包裹自定義導航欄內容,主要是通過設置pad ...
一周排行
    -Advertisement-
    Play Games
  • 一個自定義WPF窗體的解決方案,借鑒了呂毅老師的WPF製作高性能的透明背景的異形視窗一文,併在此基礎上增加了滑鼠穿透的功能。可以使得透明窗體的滑鼠事件穿透到下層,在下層窗體中響應。 ...
  • 在C#中使用RabbitMQ做個簡單的發送郵件小項目 前言 好久沒有做項目了,這次做一個發送郵件的小項目。發郵件是一個比較耗時的操作,之前在我的個人博客裡面回覆評論和友鏈申請是會通過發送郵件來通知對方的,不過當時只是簡單的進行了非同步操作。 那麼這次來使用RabbitMQ去統一發送郵件,我的想法是通過 ...
  • 當你使用Edge等瀏覽器或系統軟體播放媒體時,Windows控制中心就會出現相應的媒體信息以及控制播放的功能,如圖。 SMTC (SystemMediaTransportControls) 是一個Windows App SDK (舊為UWP) 中提供的一個API,用於與系統媒體交互。接入SMTC的好 ...
  • 最近在微軟商店,官方上架了新款Win11風格的WPF版UI框架【WPF Gallery Preview 1.0.0.0】,這款應用引入了前沿的Fluent Design UI設計,為用戶帶來全新的視覺體驗。 ...
  • 1.簡單使用實例 1.1 添加log4net.dll的引用。 在NuGet程式包中搜索log4net並添加,此次我所用版本為2.0.17。如下圖: 1.2 添加配置文件 右鍵項目,添加新建項,搜索選擇應用程式配置文件,命名為log4net.config,步驟如下圖: 1.2.1 log4net.co ...
  • 之前也分享過 Swashbuckle.AspNetCore 的使用,不過版本比較老了,本次演示用的示例版本為 .net core 8.0,從安裝使用開始,到根據命名空間分組顯示,十分的有用 ...
  • 在 Visual Studio 中,至少可以創建三種不同類型的類庫: 類庫(.NET Framework) 類庫(.NET 標準) 類庫 (.NET Core) 雖然第一種是我們多年來一直在使用的,但一直感到困惑的一個主要問題是何時使用 .NET Standard 和 .NET Core 類庫類型。 ...
  • WPF的按鈕提供了Template模板,可以通過修改Template模板中的內容對按鈕的樣式進行自定義。結合資源字典,可以將自定義資源在xaml視窗、自定義控制項或者整個App當中調用 ...
  • 實現了一個支持長短按得按鈕組件,單擊可以觸發Click事件,長按可以觸發LongPressed事件,長按鬆開時觸發LongClick事件。還可以和自定義外觀相結合,實現自定義的按鈕外形。 ...
  • 一、WTM是什麼 WalkingTec.Mvvm框架(簡稱WTM)最早開發與2013年,基於Asp.net MVC3 和 最早的Entity Framework, 當初主要是為瞭解決公司內部開發效率低,代碼風格不統一的問題。2017年9月,將代碼移植到了.Net Core上,併進行了深度優化和重構, ...