react18 核心知識點雜記1

  • 類組件 如何渲染為真實dom

入口:
const root = ReactDOM.createRoot(document.getElementById('root'))root.render(類組件)?? 類組件內部render() {return (<div>12</div>)}?? (經過babel-preset-react-app 把jsx語法,編譯為h函數形式)
React.createElement('div', null, '12')?? ( React.createElement返回值是一個普通 JavaScript 對象,即 React 元素)該元素是對UI結構以及屬性的描述。{ 	 type: 'div',props: { children: '123' },key: null,ref: null,_owner: null,_store: {}}??  然后render 將 react元素 生成為虛擬DOM 再由react內部將虛擬dom構建為樹,進行diff計算等,再創建真實dom?? componentDidMount生命周期鉤子
組件掛載完成

代碼定義 → JSX轉換 → 創建元素 → Fiber樹構建 → 協調(Diff) → DOM更新 → 生命周期回調


  • 類組件的生命周期

在這里插入圖片描述


  • vote組件 屬性初始化

class vote extends React.Component{render() {return (<div>....</div>
}
}
  • 使用vote組件
import voteconst root = ReactDOM.createRoot(document.getElementById('root'))root.render(<Vote {title = 'xxx'}/>)

在這里插入圖片描述

  • 類SetState 機制

在這里插入圖片描述

  • ????注意: react18中,setState 本身并不是傳統意義上的“異步操作”(如 Promise 或 setTimeout),而是 ?批量更新(Batching)?機制 的表現。React 內部維護一個更新隊列,在 ?同一渲染周期內 的多個 setState 會被合并處理,多個狀態更新合并(批量處理)后再統一計算新狀態和觸發渲染,這可能導致狀態更新看起來“延遲生效”
    所以本文中講的異步操作都理解為 ?批量更新(Batching)?機制
    在這里插入圖片描述
  • 🆘 react16 的setState 在合成事件 又有區別
    在這里插入圖片描述

  • react 合成事件

  handleVue() {console.log(this)}handleVue2 = (x, y) => {console.log(x, y, this)}render() {console.log('渲染完成')return (<div className="vote"><button onClick={this.handleVue}>vue</button> {/* this指向undefined */}<button onClick={() => this.handleVue()}>vue</button> {/* this指向組件實例 */}<button onClick={this.handleVue.bind(this)}>vue</button> {/* this指向組件實例 */}<button onClick={this.handleVue2.bind(1, 2)}>vue</button> {/* this指向組件實例,且預傳參 */}</div>)}

在這里插入圖片描述

  • 合成事件的事件委托機制
    在這里插入圖片描述
    合成事件都會委托給入口文件的掛載結點 #root上,合成事件與原生事件能在同一節點并行,到那時合成事件的綁定要早于原生事件,因為合成事件是react內部綁定的。

?? 注意:

事件傳播分離:合成事件與原生事件的傳播路徑獨立。阻止合成事件冒泡不會影響原生事件,反之亦然。

在這里插入圖片描述


  • purecomponent 是基于堆內存地址的淺比較,一個對象的某個深層屬性更改,它判定為地址不變,不會觸發頁面更新 ?,這是錯誤的。所以它不適合深層次的屬性的 state 或 props 的組件。

💯Hooks組件

💝 this: 因為類組件需要 react內部new 來實例化,所以要處理this綁定的問題,而函數組件是 純函數,每次渲染都會重新執行該函數組件,產生一個私有上下文,所以不涉及this的處理。

???🔥 想了解hooks組件的更新機制,先理解一下Fiber

Fiber 是 React 維護的一種數據結構,包含組件相關的所有信息。

每個React (函數 ,類) 組件都對應 一個Fiber 節點,所有的組件組成了 完整的Fiber樹。

  • React 維護兩棵 Fiber 樹:?Current Tree?(當前屏幕顯示)和 ?WorkInProgress Tree?(正在構建的更新)。
  • 狀態變更時,兩棵樹變更的節點 進行對比,復用大部分屬性?(如 type、key),僅更新變化的 props 或 state。
fiber = {type: ComponentFunction,   組件類型(函數組件)memoizedState: hook1,      **指向 Hook 鏈表的頭部**stateNode: {},             組件實例(類組件)或 DOM 節點// 其他調度相關屬性(如優先級、副作用鏈表等)
}

???🔥 再了解一下Hook

Hook(鉤子),eg:useState為一個hook函數,它的每一次調用,都會生成一個React內部維護的hook對象

const [count, setCount] = useState(0)hook對象 = {memoizedState: 0,   當前頁面展現的值baseState: 0,       所有更新(queue)處理后的新基準.當所有已處理的更新(queue 中的更新)被消費后,baseState 會被更新為最新的 memoizedState**但是** 渲染被中斷(如高優先級任務插隊),React 會回退到 baseState 重新計算狀態。queue: null,           更新隊列(存儲 setCount 觸發的更新動作)next: null,            指向下一個 Hook 的指針
};

hook對象的queue隊列作用如下:

const [count, setCount] = useState(0);
const handleClick = () => {setCount(1);       setCount(2);  
};// handleClick()后的hook對象
hook = {memoizedState: 0,  // 當前狀態值baseState: 0,      // 基礎狀態(初始值)queue: { setCount(1),setCount(2);  }, // 更新隊列next: null, // 指針
}

每次調用hook的副作用函數后,就會加入到對應hook對象的更新隊列中,等待react 內部更新。


每一個hook函數自上而下產生的hook對象 ,都會以鏈表的形式維護在 fibermemoizedState

function Component() {const [name, setName] = useState("Alice");  		   hook對象1const [age, setAge] = useState(30);                  hook對象2const [location, setLocation] = useState("Nowhere"); hook對象3
}

首次渲染(Mount)鏈表指向

Fiber.memoizedState → Hook對象1 → Hook對象2 → Hook對象3

后續渲染(Update)時,復用hook對象

Fiber.memoizedState → Hook對象1 → Hook對象2 → Hook對象3

React 再次執行組件函數,?按相同順序調用 useState
但不會重新創建 Hook 對象,而是按順序遍歷 Fiber鏈表


💘 setState對象形式 通過「閉包」鏈接hook對象

為什么要閉包?
閉包的作用是確保同一組件實例在不同渲染周期中的狀態和上下文被正確隔離
是實例狀態持久化的唯一手段。
每一個useState 執行產生的hook對象,hook中變量對外暴露,外部引用了該變量, 為一個閉包的產生。

 const [count, setCount] = useState(0)  來自React內部存儲的count,而不是組件函數的局部變量。const handleClick = () => {setCount(count + 1)  引用了count,閉包產生}

useState 簡要更新邏輯

let hook = [memoizedState: any,    // 當前狀態值(對于 useState,保存的是 state)baseState: any,        // 基礎狀態(用于計算更新)baseQueue: Update<any> | null,  // 待處理的更新隊列queue: UpdateQueue<any> | null, // 狀態更新隊列(保存 setState 觸發的更新)next: Hook | null,];function useState(newValue) {// 定義 setState 函數const setState = (newValue) => {// 收集更新階段:將更新加入當前 Hook 的隊列// 加入隊列hooks.queue.push(newValue);
------------------------------------------上下為兩個不同的階段// 處理更新階段for (const update of hooks.queue) {currentState = typeof update === 'function' ? update.action(currentState)  // 函數式更新,傳入當前狀態: newState = update.action;    // 對象式更新,直接替換值}hooks.memoizedState = currentState; // 更新最終狀態hooks.queue = [];    // 觸發重新渲染(模擬 React 的更新機制)scheduleRender();};// 返回值 和 副作用函數 setStatereturn [hook.memoizedState, setState];
}// 模擬重新渲染
function scheduleRender() {currentHookIndex = 0; // 重置索引,準備下一次渲染render();
}

?:Fiber有了,hook對象有了,閉包有了,可以來梳理 hook 是如何讓函數組件動態起來了。

1. 組件首次渲染(Mount):- Fiber 創建 → 初始化 Hook 鏈表(useState) → setState 閉包綁定 Fiber 和 Hook。2. 用戶觸發 setState:- setState 通過閉包找到對應的 Hook → 將更新加入 Hook.queue。→ 調度更新(scheduleUpdateOnFiber)。3. React 調度更新:- 進入渲染階段 → 從 Fiber 讀取 Hook 鏈表 → 按順序處理每個 Hook 的隊列。→ 計算新狀態 → 更新 Hook.memoizedState。4. 組件函數重新執行-渲染(Update):- 使用最新的狀態生成新 VDOM → 協調(Reconciliation) → 提交(Commit)到 DOM。

?? useState 返回的變量, 是函數組件外, React內部狀態在該次渲染中的狀態值快照

當組件執行,并引用了這個變量時,閉包就會形成,
所以調用棧的同步代碼,或是將要進入任務隊列的回調,都會提前綁定好該閉包的引用
當任務隊列的事件推到調用棧時,直接拿到前面已經綁定的閉包值來使用。

useState 在每次組件渲染時都會被調用,但不會重新初始化狀態(除非組件重新掛載)。它的作用是返回當前狀態值,而不是重新執行初始化邏輯


?? 注意
執行到 setstate() 這一行時,會把它注冊到 hook.queue 對象中,且不會立馬執行 (該hook由react維護,不進入堆棧,任務隊列)
接著執行后續代碼,同步的直接執行,異步的進入隊列
當調用棧清空
此時hook.queue 會先于任務隊列執行,
當hook.queue中的更新動作全部消費完,就會觸發組件重新調用渲染,
然后再執行剛剛加入任務隊列的事件。

💝所以,可以理解為一輪事件周期內,queue中的setstate() 的消費,要快于任務隊列。

???🔥 看代碼理解

const [count, setCount] = useState(0)
const handleClick = () => {setCount(count + 1)setTimeout(() => {console.log(count, 'count')})}
  1. handleclick 入調用棧
  2. setcount調用,進入hook.queue 等待批量更新,引用了count ,在堆中產生count的閉包
  3. settimeout 的回調綁定count的閉包引用(定義時綁定),再加入任務隊列
  4. 消費hook.quene ,觸發Fiber調度,更新count,渲染頁面
  5. 拉取任務隊列的定時器回調,并執行。

🧪 setState 函數式更新

與對象式不同的唯一一點就是變量的獲取

  • 對象式直接是綁定閉包引用來進行計算
  • 而函數式, 回調函數在鏈表中執行時,永遠上一個的執行后的pedding狀態,是下一個回調任務的參數。稱為鏈式狀態傳遞。

? 簡單的 setState 更新模型。
在這里插入圖片描述


flushsync

flushSync會繞過 React 的自動批處理,強制同步處理狀態更新,但僅在新舊狀態值實際變化時觸發組件渲染


?? useState 自帶性能優化

  1. ?短路優化:當新狀態與舊狀態相同(Object.is 比較)時,React 會跳過渲染。

  2. 更新隊列同一批中,多個函數式的,會基于前一個pending的狀態更改(不論是否函數式), 將最新值穿給下一個,最后觸發一次更新渲染

     例如,假設當前count是0:調用setCount(0 +1) → 計劃更新到1接著調用setCount(prev => prev +10),這里的prev是前一個pending計劃的狀態,即1,所以結果為11。
    
  3. 更新隊列同一批中,多個對象形式的,后面的覆蓋前面的,僅采取最后一次action觸發一次更新渲染

export default function Counter() {console.log('render')const [count, setCount] = useState(0)const handleClick = () => {for (let index = 0; index < 10; index++) {setCount(count + 5)}
}

1:點擊按鈕,render執行幾次?count最終為?


export default function Counter() {console.log('render')const [count, setCount] = useState(0)const handleClick = () => {for (let index = 0; index < 10; index++) {setCount(count + index)}return (<div><p>Count: {count}</p><button onClick={handleClick}>按鈕</button></div>)
}

2:點擊按鈕,render執行幾次?count最終為?


export default function Counter() {console.log('render')const [count, setCount] = useState(0)const handleClick = () => {for (let index = 0; index < 10; index++) {setCount(prev => prev + 10)}return (<div><p>Count: {count}</p><button onClick={handleClick}>按鈕</button></div>)
}

3:點擊按鈕,render執行幾次?count最終為?


export default function Counter() {console.log('render')const [count, setCount] = useState(0)const handleClick = () => {for (let index = 0; index < 10; index++) {setCount(prev => prev + index)}return (<div><p>Count: {count}</p><button onClick={handleClick}>按鈕</button></div>)
}

4:點擊按鈕,render執行幾次?count最終為?


export default function Counter() {console.log('render')const [count, setCount] = useState(0)const handleClick = () => {for (let index = 0; index < 10; index++) {flushSync(() => {setCount(count + 1)})}return (<div><p>Count: {count}</p><button onClick={handleClick}>按鈕</button></div>)
}

5:點擊按鈕,render執行幾次?count最終為?


export default function Counter() {console.log('render')const [count, setCount] = useState(0)const handleClick = () => {for (let index = 0; index < 10; index++) {flushSync(() => {setCount(prev => prev + index)})}return (<div><p>Count: {count}</p><button onClick={handleClick}>按鈕</button></div>)
}

6:點擊按鈕,render執行幾次?count最終為?


export default function Counter() {console.log('render')const [count, setCount] = useState(0)const handleClick = () => {for (let index = 0; index < 10; index++) {setCount(count + 1)setCount(prev => prev + 10)}return (<div><p>Count: {count}</p><button onClick={handleClick}>按鈕</button></div>)
}

6:點擊按鈕,render執行幾次?count最終為?


export default function Counter() {console.log('render')const [count, setCount] = useState(0)const handleClick = () => {for (let index = 0; index < 10; index++) {flushSync(() => {setCount(count + index)})}return (<div><p>Count: {count}</p><button onClick={handleClick}>按鈕</button></div>)
}

7:點擊按鈕,render執行幾次?count最終為?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/72544.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/72544.shtml
英文地址,請注明出處:http://en.pswp.cn/web/72544.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Matlab 汽車傳動系統的振動特性分析

1、內容簡介 Matlab 186-汽車傳動系統的振動特性分析 可以交流、咨詢、答疑 2、內容說明 略 摘要&#xff1a;汽車動力傳動系統是一個具有多自由度的、連續的、有阻尼系統。傳動系統的振動主要有橫向振動、扭轉振動、縱向振動。并且汽車傳動系統的扭轉振動是一個非常重要的振…

JDBC技術基礎

文章目錄 1. JDBC概述1.1 數據的持久化1.2 Java中的數據存儲技術1.3 JDBC介紹1.4 JDBC體系結構1.5 JDBC程序編寫步驟 2. 獲取數據庫連接2.1 引入JAR包2.2 要素一&#xff1a;Driver接口實現類2.2.1 Driver接口介紹2.2.2 加載與注冊JDBC驅動 2.3 要素二&#xff1a;URL2.4 要素三…

Matlab自學筆記四十八:各類型缺失值的創建、判斷、替換、移位和處理方法

1.各類數據缺失值的創建 程序示例如下&#xff1a; a[nan 1 2 3] %數值型缺失值 s[string(missing) "a" "b"] %字符串型缺失值 t[NaT datetime(2018,8,8)] %時間型缺失值 isnan(a) %判斷數值型缺失值 運行結果&#xff1a; a NaN 1 2 …

如何使用IDEA Maven構建本地jar包和POM文件?

在開發Java項目時&#xff0c;很多人會借助Maven來管理項目依賴與構建。用IntelliJ IDEA&#xff08;簡稱IDEA&#xff09;來構建本地jar包和POM文件&#xff0c;是一個常見的需求。下面我就給你詳細講解一下這個過程&#xff0c;確保你也能輕松上手&#xff01; 準備工作 首…

QT入門筆記2

目錄 一、前言 二、串口助手實現 2.1、串口 2.1.1、可用串口信息-QSerialPortInfo 2.1.2、打開串口-QSerialPort 2.1.3、串口發送接收信息 2.2、定時器-QTimer 2.3、常用屬性類型轉換&#xff08;會更新&#xff09; 2.4、子控件組規則命名優化 一、前言 這個是學習Q…

Word 小黑第40套

對應大貓43 主題 -瀏覽主題 -選擇W樣式標準文件就行 1級段落和2級段落&#xff08;用項目符號不影響原本段落文字符號 顏色修改為自動&#xff09; 整段變紅的 不是把光標定位到紅色字體那里 要選擇幾個紅色字體 再創建樣式 插入的空白頁一定要是下一頁&#xff0c;不能插空白…

基于yolo11+flask打造一個精美登錄界面和檢測系統

這個是使用flask實現好看登錄界面和友好的檢測界面實現yolov11推理和展示&#xff0c;代碼僅僅有2個html文件和一個python文件&#xff0c;真正做到了用最簡潔的代碼實現復雜功能。 測試通過環境&#xff1a; windows x64 anaconda3python3.8 ultralytics8.3.81 flask1.1.…

SQLMesh系列教程:利用date_spine宏構建日期序列實踐指南

引言&#xff1a;為什么需要日期維度表&#xff1f; 在數據分析和報表開發中&#xff0c;日期維度表是不可或缺的基礎結構&#xff0c;其中包括一定日期范圍的日期序列&#xff0c;每個序列包括對應日期屬性&#xff0c;如年季月日、是否周末等。無論是計算日粒度銷售額、分析…

【藍橋杯】省賽:神奇鬧鐘

思路 python做這題很簡單&#xff0c;靈活用datetime庫即可 code import os import sys# 請在此輸入您的代碼 import datetimestart datetime.datetime(1970,1,1,0,0,0) for _ in range(int(input())):ls input().split()end datetime.datetime.strptime(ls[0]ls[1],&quo…

2024浙江大學計算機考研上機真題

2024浙江大學計算機考研上機真題 2024浙江大學計算機考研復試上機真題 2024浙江大學計算機考研機試真題 2024浙江大學計算機考研復試機試真題 歷年浙江大學計算機復試上機真題 歷年浙江大學計算機復試機試真題 2024浙江大學計算機復試上機真題 2024浙江大學計算機復試機試真題 …

Typora 使用教程(標題,段落,字體,列表,區塊,代碼,腳注,插入圖片,表格,目錄)

標題 一個#是一級標題, 2個#是二級標題, 以此類推, 最多可達六級標題 示例 輸入#號和標題后回車即可 注意: #和標題內容之間需要存在空格(一個或多個均可), 沒有空格就會變成普通文字 標題快捷鍵 Ctrl數字 1-6 可以快速調成對應級別的標題 (選中文本/把光標放在標題上再按…

`FisherTrainer` 的自定義 `Trainer` 類:累積梯度的平方并求平均來近似計算 Fisher 信息矩陣

FisherTrainer 的自定義 Trainer 類:累積梯度的平方并求平均來近似計算 Fisher 信息矩陣 用于計算模型參數的 Fisher 信息矩陣的近似值 整體目標 Fisher 信息矩陣用于衡量模型參數的不確定性,其在優化問題中可以幫助我們更準確地更新模型參數,避免陷入局部最優。在代碼中,…

網頁制作代碼html制作一個網頁模板

制作一個簡單而實用的網頁模板&#xff1a;HTML基礎入門 在數字時代&#xff0c;網頁已成為信息展示和交流的重要平臺。HTML&#xff08;HyperText Markup Language&#xff09;作為網頁制作的基礎語言&#xff0c;為開發者提供了構建網頁的基本框架。本文將帶你了解如何使用H…

二階近似 是什么意思

二階近似 是什么意思 一、二階近似的概念與舉例 二階近似是數學分析中通過泰勒展開對函數進行近似的方法,保留到二階項(即包含一階導數和二階導數)。在優化問題(如模型訓練)中,常用于近似損失函數,幫助更精準地更新模型參數。 舉例: 假設損失函數為 L ( θ ) \mathc…

ImGui 學習筆記(四)—— 實現每窗口背景色

ImGui 的窗口背景僅通過全局的 style 控制&#xff0c;這一點不方便于我們設置特定窗口的背景透明度&#xff08;一般不用于調整顏色&#xff09;&#xff0c;分析代碼&#xff0c;我們可以找到 ImGui::RenderWindowDecorations 函數&#xff1a; void ImGui::RenderWindowDec…

Python虛擬環境完全指南:用venv管理項目依賴,避免環境沖突的N個技巧

引言&#xff1a;當你的第3個Python項目開始報錯時… “明明在Demo項目能跑的代碼&#xff0c;移植到新項目就報錯&#xff1f;” 你可能正經歷著Python開發者的成年禮——依賴沖突。本文手把手教你用Python內置的venv模塊打造隔離的虛擬環境&#xff0c;從此告別pip install引…

【后端開發面試題】每日 3 題(十三)

?個人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;專欄地址&#xff1a;https://blog.csdn.net/newin2020/category_12903849.html &#x1f4da;專欄簡介&#xff1a;在這個專欄中&#xff0c;我將會分享后端開發面試中常見的面試題給大家&#xff0c;每天的題目都是獨…

C#入門學習記錄(三)C#中的隱式和顯示轉換

C#類型轉換&#xff1a;隱式與顯式轉換的機制與應用 在C#的強類型體系中&#xff0c;數據類型轉換是實現數據交互和算法邏輯的基礎操作。當數值類型范圍存在包含關系&#xff0c;或對象類型存在繼承層次時&#xff0c;系統通過預定義的轉換規則實現類型兼容處理。隱式轉換&…

Linux FILE文件操作2- fopen、fclose、fgetc、fputc、fgets、fputs驗證

目錄 1.fopen 打開文件 1.1 只讀打開文件&#xff0c;并且文件不存在 1.2 只寫打開文件&#xff0c;并且文件不存在 1.3 只寫打開文件&#xff0c;并且文件存在&#xff0c;且有內容 1.4 追加只寫打開文件&#xff0c;并且文件不存在 2. fclose 關閉文件 3. fgetc 讀取一…

如何檢查CMS建站系統的插件是否安全?

檢查好CMS建站系統的插件安全是確保網站安全的重要環節&#xff0c;對于常見的安全檢查&#xff0c;大家可以利用以下幾種有效的方法和工具&#xff0c;來幫你評估插件的安全性。 1. 檢查插件來源和開發者信譽 選擇可信來源&#xff1a;僅從官方插件庫或可信的第三方開發者處…