React 原理篇 - 深入理解虛擬 DOM

一、什么是虛擬 DOM?

在前端開發中,“虛擬 DOM” 是一個高頻出現的術語,尤其在 React 生態中被廣泛討論。但很多開發者對它的理解往往停留在 “JS 對象” 這個表層認知上。

實際上,虛擬 DOM 是一種編程概念—— 在這個概念里,UI 以一種理想化的、“虛擬的” 表現形式被保存于內存中。它本質上是對真實 DOM 的一種描述,而不是具體的實現。

React 官方文檔對虛擬 DOM 的定義是:“一種編程概念,UI 以一種理想化的,或者說 ’ 虛擬的 ’ 表現形式被保存于內存中”。這意味著,只要能描述真實 DOM 的層次結構,無論采用何種形式(JSON、XML 等),都可以稱為虛擬 DOM。而 React 選擇了JS 對象作為這種思想的具體實現。

二、為什么需要虛擬 DOM?

在深入了解虛擬 DOM 的工作原理前,我們先思考一個問題:為什么需要虛擬 DOM?它解決了什么痛點?

2.1 性能優化:減少 DOM 操作成本

瀏覽器操作 DOM 的成本非常高,主要原因有兩點:

  • DOM 對象包含大量屬性和方法(遠多于普通 JS 對象)
  • DOM 操作會觸發瀏覽器的重排(Reflow)和重繪(Repaint)

相比之下,JS 層面的計算成本要低得多。通過先在 JS 層面對虛擬 DOM 進行計算和比對,再映射到真實 DOM,可以大幅減少不必要的 DOM 操作。

虛擬 DOM 的優勢在更新階段尤為明顯 —— 它避免了全量銷毀重建 DOM 的昂貴操作,只做必要的修改。

2.2 跨平臺渲染能力

虛擬 DOM 最大的價值之一是提供了平臺無關性。它將 UI 描述與具體渲染邏輯分離,使得同一套代碼可以在不同平臺渲染:

  • 瀏覽器環境:通過 ReactDOM 渲染為 DOM
  • 移動設備:通過 React Native 渲染為原生組件
  • 服務端:通過 ReactDOMServer 渲染為字符串
  • 測試環境:直接渲染為 JS 對象進行斷言

這種抽象能力完美契合了 “一次編寫,多端運行” 的現代開發需求。

三、React 中的虛擬 DOM 實現

在 React 中,虛擬 DOM 以 “React 元素” 的形式存在。讓我們從 JSX 開始,一步步揭開它的面紗。

3.1 JSX 與 createElement 的關系

我們編寫的 JSX 代碼:

<h1 className="title" id="header">Hello, Virtual DOM!<span>React</span>
</h1>

會被 Babel 編譯為 createElement 方法的調用:

React.createElement('h1',{ className: 'title', id: 'header' },'Hello, Virtual DOM!',React.createElement('span', null, 'React')
)

這個方法最終會返回一個描述 DOM 結構的 JS 對象 —— 這就是 React 中的虛擬 DOM:

{$$typeof: Symbol(react.element),type: 'h1',key: null,ref: null,props: {className: 'title',id: 'header',children: ['Hello, Virtual DOM!',{$$typeof: Symbol(react.element),type: 'span',// ... 其他屬性}]},_owner: null
}

3.2 createElement 核心邏輯解析

React 的 createElement 函數主要做了四件事:

  1. 處理屬性(過濾保留字、提取 key 和 ref)
  2. 處理子元素(支持多個子節點,轉為數組)
  3. 合并默認屬性(defaultProps)
  4. 創建并返回 React 元素對象

簡化版實現如下:

function createElement(type, config, children) {const props = {};let key = null;let ref = null;// 處理配置屬性if (config) {key = config.key || null;ref = config.ref || null;// 復制非保留字屬性到 propsfor (const prop in config) {if (config.hasOwnProperty(prop) && !RESERVED_PROPS.hasOwnProperty(prop)) {props[prop] = config[prop];}}}// 處理子元素const childrenLength = arguments.length - 2;if (childrenLength === 1) {props.children = children;} else if (childrenLength > 1) {props.children = Array.from(arguments).slice(2);}// 合并默認屬性if (type && type.defaultProps) {for (const prop in type.defaultProps) {if (props[prop] === undefined) {props[prop] = type.defaultProps[prop];}}}return {$$typeof: REACT_ELEMENT_TYPE,type,key,ref,props,_owner: null};
}

這個對象包含了渲染真實 DOM 所需的全部信息:元素類型(type)、屬性(props)、子元素(children)等。

四、虛擬 DOM 的工作流程

虛擬 DOM 不是孤立存在的,它是 React 渲染流程的核心環節。完整流程包括:

4.1 初始渲染

  • 從 JSX 生成虛擬 DOM
  • 將虛擬 DOM 轉換為真實 DOM 并插入頁面

4.2 狀態更新

  • 狀態變化觸發重新渲染,生成新的虛擬 DOM
  • 通過 Diff 算法對比新舊虛擬 DOM,找出差異
  • 只將差異部分應用到真實 DOM(Patch 過程)

4.3 Diff 算法:高效比對的核心

React 的 Diff 算法是虛擬 DOM 實現高性能的關鍵,它基于三個假設:

  • 不同類型的元素會產生不同的樹
  • 可以通過 key 屬性標識同一層級的子元素,保持穩定性
  • 只進行同層級比對(時間復雜度 O (n))

這種策略大幅簡化了比對過程,同時保證了大多數場景下的高效性。

五、虛擬 DOM 的局限性

盡管虛擬 DOM 帶來了諸多好處,但也存在一定問題:

  1. 初始渲染開銷:首次渲染時,虛擬 DOM 因為多了一層 JS 計算,可能比直接操作 DOM 稍慢
  2. 過度優化:對于簡單場景,手動優化的 DOM 操作可能比虛擬 DOM 更高效
  3. 內存占用:大量虛擬 DOM 對象可能占用較多內存

React 團隊也在不斷優化這些問題,例如通過 Fiber 架構實現增量渲染,進一步提升性能。

六、總結

虛擬 DOM 是 React 架構的基石,它通過 “描述式編程” 的思想,讓開發者專注于 UI 應該是什么樣子,而不是如何操作 DOM。

核心要點:

  • 虛擬 DOM 是一種思想,JS 對象是 React 中的實現方式
  • 主要優勢是減少 DOM 操作和提供跨平臺能力
  • 工作流程包括:創建虛擬 DOM → 比對差異 → 應用差異
  • React 通過 createElement 方法將 JSX 轉換為虛擬 DOM 對象

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

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

相關文章

對匯編的初理解

此處是一個簡單的函數&#xff0c;里面將調用了一個函數add&#xff08;&#xff09;函數這里是函數的原型這里是調用lcd函數產生的匯編語言&#xff0c;翻譯過來就是r11&#xff0c;r0cnt(r4cnt,前文有提及)&#xff0c;然后調用add函數&#xff0c;此處BL是指會回到指令的下一…

《Python 自動化實戰:從零構建一個文件同步工具》

《Python 自動化實戰:從零構建一個文件同步工具》 一、開篇引入:為什么我們需要文件同步? 你是否有過這樣的困擾: 公司電腦和家里電腦上都有工作項目,每次更新都要手動復制? U 盤頻繁傳輸文件,不僅麻煩還容易出錯? 項目文件夾動輒幾 G,每次同步都耗時長、效率低? 在…

工業相機與鏡頭的靶面尺寸詳解:選型避坑指南

在機器視覺系統中&#xff0c;相機與鏡頭的靶面尺寸匹配是一個非常關鍵卻又經常被忽略的細節。選錯了&#xff0c;不但影響圖像質量&#xff0c;還可能導致畫面“黑角”、視野不符、鏡頭浪費等問題。 今天我們就用通俗易懂的方式&#xff0c;聊一聊相機與鏡頭靶面尺寸的那些事兒…

使用 Go 和 go-commons 實現內存指標采集并對接 Prometheus

文章目錄一、準備工作二、編寫內存采集代碼三、運行 Exporter四、接入 Prometheus五、可擴展思路總結在運維和監控領域&#xff0c;資源指標采集 是必不可少的一環。CPU、內存、磁盤、網絡這些系統資源&#xff0c;需要實時采集并上報到監控系統中。 本文以 內存指標采集 為例&…

webrtc弱網-IntervalBudget類源碼分析與算法原理

一、核心功能 IntervalBudget 類用于基于時間窗口的帶寬預算管理。它根據設定的目標比特率&#xff08;kbps&#xff09;和一個固定時間窗口&#xff08;500ms&#xff09;&#xff0c;計算在該時間窗口內可用的字節數&#xff08;即“預算”&#xff09;&#xff0c;并支持預…

深度學習基本模塊:RNN 循環神經網絡

循環神經網絡&#xff08;RNN&#xff09;是一種專門用于處理序列數據的神經網絡架構。與處理空間數據的卷積神經網絡&#xff08;Conv2D&#xff09;不同&#xff0c;RNN通過引入循環連接使網絡具有"記憶"能力&#xff0c;能夠利用之前的信息來影響當前的輸出&#…

React18學習筆記(二) React的狀態管理工具--Redux,案例--移動端外賣平臺

文章目錄一.Redux的基礎用法1.示例:普通網頁中的Redux計步器2.Redux管理數據的流程3.配套工具和環境準備3.1.配套工具3.2.環境準備4.示例:React項目中的Redux計步器思路步驟step1:創建子模塊step2:導入子模塊step3:注入store實例step4:React組件內使用store中的數據step5:在組件…

34.Socket編程(UDP)(上)

點分十進制字符串IP 轉 32位網絡序列IP 分析&#xff1a;1&#xff09;IP轉成4字節 2&#xff09;4字節轉成網絡序列 思路&#xff1a; "192.168.1.1" 進行字符串劃分&#xff0c;以 "." 為分割符&#xff0c;分割出"192"&#xff0c;&qu…

Redis的持久化工具包—RDB AOF

文章目錄 前言 一、RDB 持久化&#xff08;快照持久化&#xff09; 1. 定義 2. RDB 觸發機制 &#xff08;1&#xff09;手動觸發 &#xff08;2&#xff09;自動觸發 3. RDB 持久化流程 4. RDB 核心配置 5. RDB 優缺點 二、AOF 持久化&#xff08;日志持久化&#xff09; 1. 定…

【Web安全】XXL-JOB框架SRC高頻漏洞分析總結

文章目錄前言一、核心漏洞分類與技術細節二、漏洞關聯利用與攻擊路徑三、版本演進與修復策略四、安全運維建議五、典型漏洞復現環境搭建六、總結前言 XXL-JOB是國內主流的開源分布式任務調度框架&#xff0c;由徐雪里開發維護&#xff0c;以輕量易用、高可用、適配分布式場景等…

Capacitor 打包后接口訪問不到的排查經歷

我最近在用 Quasar Capacitor 6 做一個 Android App&#xff0c;前端用的是 Vue3 Quasar&#xff0c;打包交給 Capacitor 去跑在手機的 WebView 里&#xff0c;后端是 FastAPI 提供接口。開發模式下一切順利&#xff0c;瀏覽器里訪問接口沒有任何問題&#xff0c;我甚至覺得打…

【正點原子】Linux應用編程入門~概念及環境介紹

應用編程概念 應用編程&#xff08;也可稱為系統編程&#xff09;與驅動編程、裸機編程有何不同&#xff1f;系統調用&#xff1b;何為庫函數&#xff1b;應用程序的 main()函數&#xff1b;應用程序開發環境的介紹&#xff1b;系統調用 定義系統調用&#xff08;system call&a…

一、HTML 完全指南:從零開始構建網頁

文章目錄前言一、 HTML 結構認識 HTML 標簽HTML 文件基本結構標簽層次結構快速生成代碼框架二、 HTML 常見標簽詳解2.1 注釋標簽2.2 標題標簽 (h1 - h6)2.3 段落標簽 (p)2.4 換行標簽 (br)2.5 格式化標簽2.6 圖片標簽 (img)2.7 超鏈接標簽 (a)2.8 表格標簽基本使用合并單元格2.…

基于POI-TL實現動態Word模板的數據填充:【散點圖】特殊處理方案

基于POI-TL實現動態Word模板的數據填充:散點圖特殊處理方案 在使用POI-TL進行Word模板動態數據填充時,圖表生成是一個常見需求。最近在項目中使用POI-TL處理散點圖時遇到了一個特殊問題,經過研究后找到了解決方案,特此記錄分享。 問題背景 POI-TL作為一款優秀的Java Wor…

使用node-Express框架寫一個學校宿舍管理系統練習項目-前后端分離

今天繼續分享一個新的練習項目&#xff0c;是使用node做為后端語言&#xff0c;來寫的一個前后端分離項目&#xff1a;學校宿舍管理系統。我們如果想掌握一門編程語言&#xff0c;就是需要大量的練習。所以當我們學習到了一些知識&#xff0c;自己想一下 可以拿學到的知識&…

Kafka 運維實戰基本操作含命令與最佳實踐

1. 基礎概覽與工具入口 Kafka 發行包的所有 CLI 工具均在 bin/ 目錄下。任何工具不帶參數運行都會顯示所有可用選項。本文命令默認&#xff1a;--bootstrap-server localhost:9092&#xff1b;生產請替換為你的控制面或內網 VIP。 2. 主題管理&#xff08;創建 / 修改 / 刪除 /…

貪心算法應用:航班起降問題詳解

Java中的貪心算法應用&#xff1a;航班起降問題詳解 貪心算法是一種在每一步選擇中都采取當前狀態下最優的選擇&#xff0c;從而希望導致全局最優解的算法策略。在航班起降問題中&#xff0c;貪心算法可以有效地解決機場跑道調度問題&#xff0c;即如何安排航班的起降順序以最大…

uniapp scroll-view 設置scrollTop無效

當我們使用 scroll-view的scroll-top的時候 默認想讓它回到頂部&#xff0c;當我們設置值為0的時候會不生效&#xff0c;在實際運用過程中&#xff0c;發現設置了scroll-top無效&#xff0c;滾動條位置并沒有發生變化&#xff0c;是因為微信小程序的官方框架處于性能考慮&#…

網絡與通信

1.TCP協議與UDP協議TCP&#xff08;Transmission Control Protocol&#xff0c;傳輸控制協議&#xff09;和 UDP&#xff08;User Datagram Protocol&#xff0c;用戶數據報協議&#xff09;是 TCP/IP 協議族中兩種核心的傳輸層協議&#xff0c;它們在數據傳輸方式、可靠性、適…

Node.js中package.json詳解

1. name&#xff08;名稱&#xff09; 如果你計劃發布你的包&#xff0c;package.json 中最重要的字段是 name 和 version&#xff0c;因為它們是必需的。name 和 version 共同組成一個假定完全唯一的標識符。包的更改應伴隨版本號的更新。如果你不打算發布包&#xff0c;那么…