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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...