虚拟 DOM介绍
虚拟 DOM(Virtual DOM)是 React 中的核心概念之一。为了理解它,首先需要知道直接操作真实的 DOM 是代价高昂的,因为这可能引起浏览器的重排和重绘,导致性能瓶颈。虚拟 DOM 旨在最小化与真实 DOM 的交互次数,从而提高性能。 以下是关于 React 中的虚拟 DOM 的详细解释:
- 定义:虚拟 DOM 是对真实 DOM 的轻量级表示。它是一个 JavaScript 对象,映射了真实 DOM 的结构和属性。
- 工作机制:
- 当 UI 的某个部分发生变化时(例如,因为用户输入或新数据),React 会创建一个新的虚拟 DOM 树。
- 接着,React 会将新的虚拟 DOM 树与上一个版本的虚拟 DOM 树进行比较,这一步被称为“差异化”(diffing)。
- 通过这一比较过程,React 能够确定真实 DOM 需要进行哪些最小的修改以反映新的 UI 状态。
- 最后,React 将这些变化批量应用到真实的 DOM,这一步被称为“重新渲染”(reconciliation)。
- 优势:
- 性能提高:通过批量更新真实 DOM,并只修改必要的部分,React 可以减少昂贵的 DOM 操作和不必要的重排/重绘。
- 编程简化:开发者不必关心如何高效地更新 DOM。他们只需声明 UI 在各种状态下的外观,而 React 负责如何实现这一过程。
- 跨平台:由于虚拟 DOM 只是 JavaScript 对象,因此 React 可以在不同环境中重复使用相同的逻辑,例如在 React Native 中。
- 局限性:
- 虽然虚拟 DOM 提高了性能,但它并不是解决所有问题的银弹。在某些情况下,对真实 DOM 的直接操作可能更快。
- 差异化和重新渲染仍然需要计算资源。对于大型应用或频繁的更新,开发者可能需要使用一些性能优化技巧。 总之,虚拟 DOM 是 React 如何快速和高效地更新 UI 的核心机制。通过将复杂的 DOM 更新逻辑抽象为简单的 JavaScript 运算,React 提供了一个强大且高性能的方法来构建动态 web 应用。
模拟创建虚拟 DOM——createElement
封装createElement
src\react.js
function createElement(type, config, children) {
let props = { ...config };
if (arguments.length > 3) {
props.children = Array.prototype.slice.call(arguments, 2);
} else {
props.children = children;
}
return {
type,
props,
};
}
const React = {
createElement,
};
export default React;
引入createElement创建虚拟DOM
src\index.js
import React from "./react";
const element = React.createElement(
"div",
{
style: {
color: "red",
},
className: "wrapper",
},
"hello",
React.createElement(
"span",
{
style: {
color: "blue",
},
},
"world"
)
);
console.log(JSON.stringify(element));
输出结果:
{
"type": "div",
"props": {
"style": { "color": "red" },
"className": "wrapper",
"children": [
"hello",
{
"type": "span",
"props": { "style": { "color": "blue" }, "children": "world" }
}
]
}
}
模拟渲染虚拟 DOM——createRoot
封装createRoot
src\react-dom\client.js
function createRoot(container) {
return {
render(reactElement) {
const domElement = renderElement(reactElement);
container.appendChild(domElement);
},
};
}
function renderElement(element) {
if (typeof element === "string") {
return document.createTextNode(element);
}
const { type, props } = element;
const domElement = document.createElement(type);
Object.keys(props).forEach((name) => {
if (name === "children") {
return;
}
if (name === "style") {
Object.assign(domElement.style, props.style);
} else if (name.startsWith("on")) {
const eventName = name.toLowerCase().substring(2);
domElement.addEventListener(eventName, props[name]);
} else {
domElement[name] = props[name];
}
});
if (typeof props.children !== "undefined") {
const children = Array.isArray(props.children)
? props.children
: [props.children];
children.forEach((child) => domElement.appendChild(renderElement(child)));
}
return domElement;
}
const ReactDOMClient = {
createRoot,
};
export default ReactDOMClient;
引入createRoot渲染虚拟 DOM
src\index.js
import React from "./react";
import ReactDOM from "./react-dom/client";
const element = React.createElement(
"div",
{
style: {
color: "red",
},
className: "wrapper",
},
"hello",
React.createElement(
"span",
{
style: {
color: "blue",
},
},
"world"
)
);
ReactDOM.createRoot(document.getElementById("root")).render(element);
渲染结果:

image.png