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
  • 背景 在瀏覽器中訪問本地靜態資源html網頁時,可能會遇到跨域問題如圖。 是因為瀏覽器預設啟用了同源策略,即只允許載入與當前網頁具有相同源(協議、功能變數名稱和埠)的內容。 WebView2預設情況下啟用了瀏覽器的同源策略,即只允許載入與主機相同源的內容。所以如果我們把靜態資源發佈到iis或者通過node ...
  • 最近看幾個老項目的SQL條件中使用了1=1,想想自己也曾經這樣寫過,略有感觸,特別拿出來說道說道。編寫SQL語句就像炒菜,每一種調料的使用都會影響菜品的最終味道,每一個SQL條件的加入也會影響查詢的執行效率。那麼 1=1 存在什麼樣的問題呢?為什麼又會使用呢? ...
  • 好久不見,我又回來了。 給大家分享一個我最近使用c#代碼操作ftp伺服器的代碼示例: 1 public abstract class FtpOperation 2 { 3 /// <summary> 4 /// FTP伺服器地址 5 /// </summary> 6 private string f ...
  • 一:背景 1. 講故事 過年喝了不少酒,腦子不靈光了,停了將近一個月沒寫博客,今天就當新年開工寫一篇吧。 去年年初有位朋友找到我,說他們的系統會偶發性崩潰,在網上也發了不少帖子求助,沒找到自己滿意的答案,讓我看看有沒有什麼線索,看樣子這是一個牛皮蘚的問題,既然對方有了dump,那就分析起來吧。 二: ...
  • 自己製作的一個基於Entity Framework Core 的資料庫操作攔截器,可以列印資料庫執行sql,方便開發調試,代碼如下: /// <summary> /// EF Core 的資料庫操作攔截器,用於在資料庫操作過程中進行日誌記錄和監視。 /// </summary> /// <remar ...
  • 本文分享自華為雲社區《Go併發範式 流水線和優雅退出 Pipeline 與 Cancellation》,作者:張儉。 介紹 Go 的併發原語可以輕鬆構建流數據管道,從而高效利用 I/O 和多個 CPU。 本文展示了此類pipelines的示例,強調了操作失敗時出現的細微之處,並介紹了乾凈地處理失敗的 ...
  • 在上篇文章中,我們介紹到在多線程環境下,如果編程不當,可能會出現程式運行結果混亂的問題。出現這個原因主要是,JMM 中主記憶體和線程工作記憶體的數據不一致,以及多個線程執行時無序,共同導致的結果。 ...
  • 1、下載安裝包首先、進入官網下載安裝包網址:https://www.python.org/downloads/windows/下載步驟:進入下載地址,根據自己的電腦系統選擇相應的python版本 選擇適配64位操作系統的版本(查看自己的電腦操作系統版本), 點擊下載安裝包 也可以下載我百度雲分享的安 ...
  • 簡介 git-commit-id-maven-plugin 是一個maven 插件,用來在打包的時候將git-commit 信息打進jar中。 這樣做的好處是可以將發佈的某版本和對應的代碼關聯起來,方便查閱和線上項目的維護。至於它的作用,用官方說法,這個功能對於大型分散式項目來說是無價的。 功能 你 ...
  • 序言 在數字時代,圖像生成技術正日益成為人工智慧領域的熱點。 本討論將重點聚焦於兩個備受矚目的模型:DALL-E和其他主流AI繪圖方法。 我們將探討它們的優勢、局限性以及未來的發展方向。通過比較分析,我們期望能夠更全面地瞭解這些技術,為未來的研究和應用提供啟示。 Q: 介紹一下 dall-e Ope ...