对React官网《Virtual DOM 及内核》注解:其实Virtual DOM 就藏在在代码行间

原文链接

什么是 Virtual DOM?

Virtual DOM 是一种编程概念。在这个概念里, UI 以一种理想化的,或者说"虚拟的"表现形式被保存于内存中,并通过如 ReactDOM 等类库使之与"真实的" DOM 同步。这一过程叫做协调

js 复制代码
使用"虚拟的"表现形式被保存与内存中,其实在我们写代码的过程中很常见,也经常会用到。
就比如之前流行的低代码,如果是你,你会怎么实现。
​
💡 用低代码平台理解Virtual DOM
想象你要实现一个低代码平台,用户通过拖拽组件来构建页面。你会怎么设计?
// 用户拖拽后,我们在内存中保存这样的数据结构
const pageConfig = {
  type: 'div',
  props: { className: 'container' },
  children: [
    {
      type: 'h1',
      props: { style: { color: 'blue' } },
      children: ['欢迎使用低代码平台']
    },
    {
      type: 'button',
      props: { 
        onClick: () => alert('点击了按钮'),
        className: 'btn-primary'
      },
      children: ['点击我']
    }
  ]
}

这个pageConfig就是"虚拟的"表现形式!我们把UI的结构和状态保存在内存中,然后通过渲染引擎将其转换为真实的DOM。

js 复制代码
// 渲染函数:将虚拟结构转换为真实DOM
function render(vnode, container) {
  if (typeof vnode === 'string') {
    return document.createTextNode(vnode);
  }
  
  const element = document.createElement(vnode.type);
  
  // 设置属性
  Object.keys(vnode.props || {}).forEach(key => {
    if (key === 'onClick') {
      element.addEventListener('click', vnode.props[key]);
    } else if (key === 'className') {
      element.className = vnode.props[key];
    } else if (key === 'style') {
      Object.assign(element.style, vnode.props[key]);
    }
  });
  
  // 递归渲染子元素
  (vnode.children || []).forEach(child => {
    element.appendChild(render(child, element));
  });
  
  return element;
}
​
// 使用
const realDOM = render(pageConfig, document.body);
document.body.appendChild(realDOM);

这就是Virtual DOM的核心思想:用JavaScript对象描述UI结构,然后通过程序将其转换为真实DOM

📝 常见的"虚拟化"场景

其实在开发中,我们经常使用这种"虚拟化"的思想:

JSON配置驱动的表单

js 复制代码
const formConfig = {
  fields: [
    { type: 'input', name: 'username', label: '用户名', required: true },
    { type: 'select', name: 'gender', label: '性别', options: ['男', '女'] }
  ]
}

路由配置

js 复制代码
const routes = [
  { path: '/home', component: Home },
  { path: '/user/:id', component: UserDetail }
]

数据库ORM

js 复制代码
const user = {
  name: '张三',
  age: 25,
  email: 'zhangsan@example.com'
}
// 最终转换为SQL: INSERT INTO users (name, age, email) VALUES (...)

这些都是将抽象的配置或数据结构转换为具体实现的例子。

Virtual DOM 是一种 以 JS 对象的形式 表达页面结构的方式,本质上是用 JavaScript 模拟 DOM 结构的"影子副本",对真实DOM进行映射与管理。

这种方式赋予了 React 声明式的 API:您告诉 React 希望让 UI 是什么状态,React 就确保 DOM 匹配该状态。这使您可以从属性操作、事件处理和手动 DOM 更新这些在构建应用程序时必要的操作中解放出来。

js 复制代码
💡 注解:这里说的"声明式API"是相对于"命令式"而言的。
​
命令式:告诉程序"怎么做"
function updateUserList(users) {
  const list = document.getElementById('user-list');
  list.innerHTML = ''; // 先清空
  
  users.forEach(user => {
    const li = document.createElement('li');
    li.textContent = user.name;
    li.addEventListener('click', () => showUserDetail(user.id));
    list.appendChild(li);
  });
}
声明式:告诉程序"要什么"
function UserList({ users }) {
  return (
    <ul>
      {users.map(user => (
        <li key={user.id} onClick={() => showUserDetail(user.id)}>
          {user.name}
        </li>
      ))}
    </ul>
  );
}
声明式让我们专注于"最终要什么样的UI",而不用关心"如何一步步操作DOM来达到这个效果"。
React会自动处理DOM的创建、更新、删除等操作,我们只需要描述每个状态下UI应该是什么样子。

与其将 "Virtual DOM" 视为一种技术,不如说它是一种模式,人们提到它时经常是要表达不同的东西。

js 复制代码
💡 注解:这句话很重要!为什么说Virtual DOM是一种"模式"而不是"技术"?
​
技术通常指具体的实现方案,比如:
- React的Virtual DOM实现
- Vue的Virtual DOM实现  
- Preact的Virtual DOM实现
​
但模式是一种设计思想,可以有多种实现方式:
​
1. 数据驱动视图的模式:
   - 状态 → 虚拟表示 → 真实DOM
   - 这个模式不仅限于前端,在游戏开发、GUI开发中都有应用
​
2. 不同的"虚拟"实现:
   - React用JavaScript对象表示
   - Flutter用Widget树表示
   - 游戏引擎用场景图表示
​
3. 相同模式,不同表达:
   - 有人说Virtual DOM是为了性能优化
   - 有人说Virtual DOM是为了开发体验
   - 有人说Virtual DOM是为了跨平台渲染
   - 这些都对,因为它们都是这个模式的不同价值体现
​
所以当我们讨论Virtual DOM时,重要的是理解这种"用数据描述UI,然后同步到真实渲染目标"的模式思想。

在 React 的世界里,术语 "Virtual DOM" 通常与 React 元素关联在一起,因为它们都是代表了用户界面的对象。而 React 也使用一个名为 "fibers" 的内部对象来存放组件树的附加信息。上述二者也被认为是 React 中 "Virtual DOM" 实现的一部分。

js 复制代码
💡 注解:这里提到了两个重要概念,让我们分别理解一下:
​
1. React元素(React Elements):
// 当你写JSX时
const element = <h1 className="greeting">Hello, world!</h1>;
​
// 实际上创建的是这样的对象
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world!'
  },
  key: null,
  ref: null
};

这就是React元素,它是Virtual DOM的基本单位。

js 复制代码
2. Fibers:
React 16引入的新架构,可以理解为"升级版的Virtual DOM节点":
const fiberNode = {
  type: 'div',                    // 元素类型
  props: { className: 'container' }, // 属性
  child: childFiber,              // 第一个子节点
  sibling: siblingFiber,          // 兄弟节点
  return: parentFiber,            // 父节点
  
  // Fiber特有的调度信息
  effectTag: 'UPDATE',            // 更新标记
  expirationTime: 1234567890,     // 过期时间
  alternate: oldFiber,            // 上一次的Fiber节点
  
  // 组件状态
  memoizedState: componentState,  // 缓存的状态
  memoizedProps: props,           // 缓存的props
}
Fiber让React能够:
- 时间切片:将大任务拆分成小任务,避免阻塞主线程
- 优先级调度:紧急更新可以打断低优先级更新
- 并发渲染:支持Suspense、并发特性等
​
所以Virtual DOM实际上包含了React元素和Fiber两个层面的实现。

Shadow DOM 和 Virtual DOM 是一回事吗?

不,他们不一样。Shadow DOM 是一种浏览器技术,主要用于在 web 组件中封装变量和 CSS。Virtual DOM 则是一种由 Javascript 类库基于浏览器 API 实现的概念。

js 复制代码
💡 注解:这两个概念经常被混淆,让我们用代码来看看区别:
​
Shadow DOM(浏览器原生API):
class MyButton extends HTMLElement {
  constructor() {
    super();
    // 创建Shadow DOM,实现真正的样式和DOM隔离
    const shadow = this.attachShadow({ mode: 'open' });
    shadow.innerHTML = `
      <style>
        button { color: red; } /* 这个样式被封装,不会影响外部 */
      </style>
      <button><slot></slot></button>
    `;
  }
}
customElements.define('my-button', MyButton);
Virtual DOM(JavaScript库概念):
const virtualButton = {
  type: 'button',
  props: {
    style: { color: 'red' },
    children: ['点击我']
  }
};
// 通过React的渲染引擎转换为真实DOM
ReactDOM.render(virtualButton, container);
简单对比:
特性 Shadow DOM Virtual DOM
性质 浏览器原生API JavaScript库概念
作用 样式和DOM封装隔离 性能优化和开发体验
隔离 真实的样式和DOM隔离 抽象层,最终还是操作真实DOM
应用场景 Web Components React、Vue等框架
html 复制代码
Shadow DOM解决的是组件样式隔离问题,Virtual DOM解决的是DOM操作性能和开发体验问题。

总结

html 复制代码
💡 Virtual DOM的核心价值在于:
1. 抽象化:用JavaScript对象描述UI结构
2. 声明式:专注于"要什么"而不是"怎么做"  
3. 自动化:框架负责DOM操作的优化
4. 可预测:相同的数据总是产生相同的UI
​
这种模式让我们能够用更直观的方式构建复杂的用户界面,同时获得更好的性能和开发体验。
相关推荐
brzhang26 分钟前
我操,终于有人把 AI 大佬们 PUA 程序员的套路给讲明白了!
前端·后端·架构
止观止1 小时前
React虚拟DOM的进化之路
前端·react.js·前端框架·reactjs·react
goms1 小时前
前端项目集成lint-staged
前端·vue·lint-staged
谢尔登1 小时前
【React Natve】NetworkError 和 TouchableOpacity 组件
前端·react.js·前端框架
Lin Hsüeh-ch'in1 小时前
如何彻底禁用 Chrome 自动更新
前端·chrome
augenstern4163 小时前
HTML面试题
前端·html
张可3 小时前
一个KMP/CMP项目的组织结构和集成方式
android·前端·kotlin
G等你下课4 小时前
React 路由懒加载入门:提升首屏性能的第一步
前端·react.js·前端框架
谢尔登4 小时前
【React Native】ScrollView 和 FlatList 组件
javascript·react native·react.js
蓝婷儿5 小时前
每天一个前端小知识 Day 27 - WebGL / WebGPU 数据可视化引擎设计与实践
前端·信息可视化·webgl