Chen Yu Ling
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
Publish Note

Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

Your note will be visible on your profile and discoverable by anyone.
Your note is now live.
This note is visible on your profile and discoverable online.
Everyone on the web can find and read all notes of this public team.
See published notes
Unpublish note
Please check the box to agree to the Community Guidelines.
View profile
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
# 什麼是 Virtual DOM? 從底層原理到 React 的渲染策略 ## 為什麼要談 Virtual DOM? 每當我們在網頁上看到動態更新的內容,背後都可能涉及了對 DOM (文件物件模型) 的操作。然而,DOM 與瀏覽器的渲染引擎是緊密耦合的,任何一次看似微小的改動,都可能觸發瀏覽器進行成本高昂的 重排 (Reflow) 與 重繪 (Repaint),頻繁的操作最終會成為應用的效能瓶頸。 為了在提供豐富互動的同時,又能維持流暢的體驗,前端框架必須找到一個方法來「最小化對真實 DOM 的操作」。而 Virtual DOM,正是為此而生的一種優雅策略。 ## DOM 是什麼?為什麼慢? 很間單的先用一句話說明是:瀏覽器呈現 UI 的載體。但在我整理資料之後我覺得可以用更精細的說明: 1. 網頁瀏覽器內部的 HTML 解析器(parsing)會將原始的 HTML/XML 文本解析並轉換為一個樹狀的資料結構,這就是 Document Object Model (DOM)。 2. DOM 的每個節點都代表文件中的一部分,並以 JavaScript 物件的形式存在,提供了一系列屬性與方法 (DOM API)。 3. JavaScript 正是透過這些 DOM API 來存取、查詢、修改這些節點,從而動態地控制網頁的內容、樣式和行為,實現使用者互動。 ### 什麼是 Reflow / Repaint? - **回流 (Reflow)**:當 DOM 元素的幾何屬性(如寬度、高度、位置)發生改變,導致瀏覽器需要重新計算元素在頁面上的佈局時,就會觸發回流。 - **重繪 (Repaint)**:當元素的外觀屬性(如顏色、背景)發生改變,但不影響其佈局時,瀏覽器只需重新繪製該部分的外觀,這個過程稱為重繪。 - 回流必定會觸發重繪,但重繪不一定會觸發回流。因此,回流對效能的影響遠大於重繪。 ### 每次 DOM 變化都可能觸發效能瓶頸 瀏覽器在操作 DOM 的過程中都有可能引發 Reflow 或 Repaint,過程中是仰賴 瀏覽器CPU 需要重新計算,在反覆的過程中是可能引起效能瓶頸的。 ```mermaid graph TD A[HTML 檔案] --> C[HTML 解析器] B[CSS 檔案] --> D[CSS 解析器] C --> E(DOM Tree) D --> F(CSSOM Tree) E --> G[結合 Combine] F --> G G --> H[Render Tree<br/>只包含可見元素<br/>排除 display: none] H --> I[佈局 Layout<br/>計算位置和尺寸] I --> J[繪製 Paint<br/>填充像素] J --> K[合成 Composite<br/>圖層合併] K --> L[螢幕顯示] subgraph "動態更新循環" M[JavaScript] M -- 修改 DOM --> E M -- 修改樣式 --> N[CSSOM 更新] N --> H E -. 觸發 Reflow .-> I H -. 觸發 Repaint .-> J I -. 觸發 Repaint .-> J J -. 觸發 Composite .-> K end style A fill:#e3f2fd style B fill:#e3f2fd style E fill:#f3e5f5 style F fill:#f3e5f5 style H fill:#e8f5e8 style I fill:#fff3e0 style J fill:#ffebee style K fill:#f1f8e9 style L fill:#e8eaf6 style M fill:#fce4ec style N fill:#f3e5f5 ``` ☝️DOM Tree + 瀏覽器渲染流程 ## Virtual DOM 是什麼? Virtual DOM 是一個存於記憶體中的 JavaScript 物件,它用來描述真實 DOM 的結構。你可以把它想像成一張 UI 的「設計藍圖」。 這張「藍圖」記錄了每個節點的類型 (例如 div)、屬性 (例如 className: 'title') 以及它的子節點。當應用程式的狀態改變時,框架會建立一張新的「藍圖」,並比較新舊藍圖的差異,然後只針對真正有變動的部分去更新真實的 DOM。這個過程避免了直接且頻繁地操作笨重的真實 DOM。 ### 真實 DOM 結構 vs. JavaScript 表示的 Virtual DOM ```html <div class="container"> <h1 id="title">Hello</h1> <p>Virtual DOM</p> </div> ``` ↕️ ```javascript // 這是 Virtual DOM 的樣子 // (一個巢狀的 JavaScript 物件) const vdom = { tagName: 'div', props: { className: 'container' }, children: [ { tagName: 'h1', props: { id: 'title' }, children: ['Hello'] }, { tagName: 'p', props: {}, children: ['Virtual DOM'] } ] }; ``` ## Virtual DOM 的核心機制 Virtual DOM 的威力展現在UI 更新的生命週期中。我們可以將整個過程拆解成兩大階段: - 階段一:首次渲染 (Initial Render) 當頁面首次載入時,框架會根據初始狀態建立第一版的 Virtual DOM。接著,它會以此為藍圖,完整地渲染出真實的 DOM 結構,這就是我們在畫面上看到的樣子。 - 階段二:更新與協調 (Update & Reconciliation) 當應用程式的狀態改變時 (例如,使用者點擊按鈕),神奇的事情發生了: 1. 產生新樹 (Generate): 框架會根據新的狀態,在記憶體中建立一棵全新的 Virtual DOM Tree。 2. 比較差異 (Diff): 接著,最關鍵的 Diffing (差異比對) 演算法會上場。它會高效地比較「新的 Virtual DOM」和「更新前的舊 Virtual DOM」,找出兩者之間最小的差異點 (例如:一個節點被新增、一個屬性被修改)。 3. 批次更新 (Patch): 框架會將這些差異彙整成一個「補丁」,然後只針對這些變動的部分去執行最小化的真實 DOM 操作。這個過程稱為 Patch。 透過這套「在記憶體中計算差異,再一次性更新真實 DOM」的機制,Virtual DOM 大幅減少了直接操作 DOM 的次數,從而避免了昂貴的 Reflow 和 Repaint,提升了應用程式的效能。 ![Screenshot 2025-07-06 at 23.55.08](https://hackmd.io/_uploads/Hy_vnGuHge.png) ### Virtual DOM 的核心思想: 在記憶體中完成比較,然後只對真實 DOM 進行最小化的必要修改。 看圖的人可以立刻明白,整個 DOM 並沒有被重新創建,只有黃色那一塊被「Patch」上去了。 ## Virtual DOM 的優勢與限制 ### 優點: 1. **快**: 將複雜且耗時的 DOM 比對操作,從相對緩慢的瀏覽器渲染引擎層,完全轉移到了高速的 JavaScript 引擎中。在記憶體裡比較兩個 JavaScript 物件的差異,遠比直接查詢、操作真實 DOM 樹來得快。 2. **可以找出最小操作**: 這是 Virtual DOM 最核心的價值。透過 Diff 演算法,它能精準計算出「真正需要改變」的最小集合,從而避免了大量不必要的 Reflow 與 Repaint,將瀏覽器的效能浪費降到最低。 3. 提升開發體驗與抽象化: Virtual DOM 讓開發者可以從繁瑣的 DOM 操作中解放出來。我們只需要以「宣告式」的方式描述 UI 應該長什麼樣子,而不用去關心具體的 DOM 操作步驟。框架會自動處理中間所有的髒活。 4. **批次更新與跨平台潛力**: 因為所有的變動都會先在 Virtual DOM 層計算,框架可以將一段時間內的多個狀態變更「批次處理」(Batching),再一次性更新到真實 DOM,進一步提升效能。同時,因為 Virtual DOM 本身是一個獨立於瀏覽器的 JavaScript 物件,這也為跨平台渲染(例如在 React Native 中渲染成原生元件)提供了可能性。 ### 缺點: 1. **記憶體開銷**: 在記憶體中維護一個完整的 DOM 複製品,無疑會佔用更多的記憶體。對於一些極端輕量或記憶體受限的應用場景,這可能會成為一個考量點。 2. **並非總是更快**: Virtual DOM 的優勢體現在「頻繁且複雜」的 UI 更新上。對於首次渲染,它多了一層計算,理論上會比直接產生 DOM 慢一點。對於那些只有極少量更新的簡單頁面,直接操作 DOM 的成本可能更低。 3. **演算法成本與 `key`**: Diff 演算法本身雖然高效,但依然有其計算成本。尤其是在處理列表時,如果沒有為每個子元素提供一個穩定且唯一的 `key`,React 可能無法有效複用節點,甚至會採用效率更低的策略(例如:銷毀整個列表再重建),導致效能不升反降,這是在開發中必須注意的陷阱。 ## 延伸觀點:React 如何駕馭 Virtual DOM? 前面我們探討了 Virtual DOM 的通用概念,現在讓我們聚焦於 React 是如何將這個概念變成一個強大、高效的渲染策略。 ### 從 JSX 到 React Element:藍圖的繪製 在 React 中,構成 Virtual DOM 的最小單位被稱為 React Element。它就是一個輕量的 JavaScript 物件,用來描述你想在畫面上看到什麼。 React 提供了 createElement 函式來產生這些物件。 ```javascript const element = createElement(type, props, ...children) /** * 參數分別對應:元素類型、屬性、子元素資訊 */ ``` 例如,要描述一個按鈕,程式碼如下: ```javascript import { createElement } from 'react'; const buttonElement = createElement( 'button', { className: 'primary' }, 'Click Me!' ); ``` 顯然,如果整個應用程式都這樣寫,程式碼會變得非常難以閱讀。為此,React 引入了 JSX。它是一種語法糖,讓我們能用類似 HTML 的語法來「宣告」UI,編譯後會自動轉換為 createElement 的呼叫。 > 重點:JSX 並不是 HTML,它是一種更直觀的、用來建立 React Element 的語法。 透過 JSX 表達一顆按鈕 virtual DOM: ```jsx const buttonElement = <button className="primary"> Click Me! </button>; // ...會被轉譯成上面那段 React.createElement(...) 的程式碼。 ``` ### Reconciliation:框架更新畫面的藝術 當框架需要根據新的狀態藍圖(Virtual DOM)來更新實際的 DOM 時,這個「使兩者保持同步」的過程,廣義上可以稱為 Reconciliation (協調)。 這並不是 React 獨有的概念。 許多採用 Virtual DOM 的框架(例如 Vue)也都有自己的協調機制,只是具體的實現細節和演算法會有所不同。 不過,「Reconciliation」這個詞因為 React 官方文件的大量使用而廣為人知。在 React 的世界裡,它特指其內部一套包含 Diffing 演算法 和 Commit 流程 的完整畫面更新策略。接下來,我們就來看看 React 是如何實現它的 Reconciliation 的。 當我們透過 setState 觸發更新時,React 會啟動一個名為 Reconciliation (協調) 的過程,這正是 React 更新畫面的核心機制。這個過程可以被簡化為兩個主要階段: **1. Render Phase (渲染階段):** - 觸發: 當狀態改變,React 會重新執行相關的組件函式。 - 工作: 產生一棵新的 React Element Tree (新的 Virtual DOM)。 - 比對: 在這個階段,React 會執行 Diffing 演算法,比較新舊兩棵樹,找出所有差異點。 - 這個階段是純粹的計算,可以被暫停或中斷,且不會產生任何副作用(不會修改真實 DOM)。 **2. Commit Phase (提交階段):** - 觸發: Render Phase 完成後,React 會得到一份包含所有變更的「清單」。 - 工作: React 會一次性地將這些變更提交 (Commit) 到真實 DOM 上,執行所有必要的 DOM 新增、刪除和更新操作。這個過程就是我們所說的 Patching。 - 這個階段會直接操作 DOM,因此是同步執行的,不能被打斷。 React 正是透過 JSX 讓我們能輕鬆地描述 Virtual DOM,再透過 Render Phase 在記憶體中高效地計算出差異,最後在 Commit Phase 一次性地完成最小化的真實 DOM 更新。 ## 總結 **什麼是 Virtual DOM?** 它是一種為了解決「直接操作 DOM 成本過高」而生的效能優化模式。其核心思想是,在 JavaScript 記憶體中建立一個輕量的 DOM 結構「藍圖」,當狀態變更時,透過高效的 Diff (比對) 演算法計算出最小差異,再將這些差異一次性 Patch (更新) 到真實 DOM 上,從而避免了頻繁且昂貴的瀏覽器重排與重繪。 **React 是如何運用 Virtual DOM?** React 是將此模式發揚光大的實踐者。它透過 JSX 作為語法糖,讓開發者能宣告式地定義 UI,最終生成構成 Virtual DOM 的 React Element。其核心的 Reconciliation (協調) 過程被分為兩個階段:在 Render Phase 中計算差異,並在 Commit Phase 中將變更應用至 DOM,這套精密的機制正是 React 高效渲染的基石。 ## 參考資料 1. [Document Object Model ](https://en.wikipedia.org/wiki/Document_Object_Model) 2. [回流 (Reflow) 和重繪 (Repaint) 是什麼?以及如何優化?](https://www.explainthis.io/zh-hant/swe/repaint-and-reflow) 3. [Virtual DOM & V-Node](https://ithelp.ithome.com.tw/articles/10193220?sc=iThelpR) 4. [Rendering Mechanism](https://vuejs.org/guide/extras/rendering-mechanism.html) 5. [Framework main features](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Frameworks_libraries/Main_features) 6. [createElement](https://react.dev/reference/react/createElement) 7. [ React 畫面更新的核心機制(上):一律重繪渲染策略](https://ithelp.ithome.com.tw/articles/10298007) 8. [React 畫面更新的核心機制(下):Reconciliation](https://ithelp.ithome.com.tw/articles/10298053)

Import from clipboard

Paste your markdown or webpage here...

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.
Upgrade
All
  • All
  • Team
No template.

Create a template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

Slide Example

API Docs

Edit in VSCode

Install browser extension

Contacts

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Get Full History Access

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

Note content is identical to the latest version.
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully