答案:实现虚拟DOM需创建VNode、渲染真实节点与diff算法。首先用h函数生成描述UI的JS对象,再通过mount递归构建真实DOM并保留引用;当数据变化时,patch比较新旧VNode:标签不同则替换,相同则更新属性并递归处理子节点,最后通过render和update驱动视图更新,核心是声明式UI与最小化更新。

构建一个类似 React 的虚拟 DOM 系统,核心是理解“声明式 UI”和“高效的 DOM 更新机制”。关键在于:用 JavaScript 对象描述 UI(即虚拟 DOM),通过对比新旧对象找出最小变更,再应用到真实 DOM 上。下面带你一步步实现一个极简但完整的虚拟 DOM 系统。
虚拟 DOM 本质是一个普通的 JS 对象,用来描述真实 DOM 的结构。我们约定每个 vnode 包含标签名、属性和子节点。
function h(tag, props, children) {
return {
tag,
props: props || {},
children: children || []
};
}
比如 h('div', { id: 'app' }, [h('span', {}, ['Hello'])]) 就会生成一个描述 div#app > span 结构的树形对象。
需要一个函数把 vnode 转换成真实的 DOM 节点,并插入页面。
function mount(vnode, container) {
const el = document.createElement(vnode.tag);
// 设置属性
Object.keys(vnode.props).forEach(key => {
el[key] = vnode.props[key];
});
// 递归挂载子节点
(vnode.children || []).forEach(child => {
if (typeof child === 'string') {
el.appendChild(document.createTextNode(child));
} else {
mount(child, el);
}
});
container.appendChild(el);
vnode.el = el; // 保留对真实节点的引用
}
这个过程叫做“初次渲染”,从根 vnode 开始递归创建 DOM 并挂载。
当数据变化时,会生成新的 vnode 树。我们需要比较新旧两棵树,找出差异并更新真实 DOM。
为了高效,通常采用“同层比较”策略(React 也这么做):
function patch(oldVNode, newVNode) {
const el = (newVNode.el = oldVNode.el);
if (oldVNode.tag !== newVNode.tag) {
// 标签不同,替换整棵子树
el.parentNode.replaceChild(
mount(newVNode), el
);
return;
}
// 标签相同,更新属性
const oldProps = oldVNode.props || {};
const newProps = newVNode.props || {};
Object.keys(newProps).forEach(key => {
if (oldProps[key] !== newProps[key]) {
el[key] = newProps[key];
}
});
Object.keys(oldProps).forEach(key => {
if (!(key in newProps)) {
el[key] = null;
}
});
// 比较子节点
const oldChildren = oldVNode.children || [];
const newChildren = newVNode.children || [];
const commonLength = Math.min(oldChildren.length, newChildren.length);
for (let i = 0; i < commonLength; i++) {
patch(oldChildren[i], newChildren[i]);
}
// 新增子节点
for (let i = commonLength; i < newChildren.length; i++) {
mount(newChildren[i], el);
}
// 移除多余旧子节点
for (let i = commonLength; i < oldChildren.length; i++) {
el.removeChild(oldChildren[i].el);
}
}
最后,封装一个简单的“组件”更新流程:
let currentVNode = null;
function render(vnode, container) {
if (!currentVNode) {
// 初次渲染
currentVNode = vnode;
mount(vnode, container);
} else {
// 更新
const prevVNode = currentVNode;
currentVNode = vnode;
patch(prevVNode, vnode);
}
}
现在你可以这样使用:
const App = (count) => h('div', { id: 'app' }, [
h('p', {}, [`Count: ${count}`]),
h('button', {
onclick: () => update(++count)
}, ['+'])
]);
let count = 0;
const update = () => render(App(count), document.getElementById('root'));
update(); // 首次渲染
基本上就这些。虽然简化了很多细节(如 key 优化、事件代理、批量更新等),但已经涵盖了 React 虚拟 DOM 的核心思想:用 JS 对象描述 UI,通过 diff 找出最小变更,高效更新视图。不复杂但容易忽略的是,保持对真实 DOM 节点的引用和递归 patch 的边界控制。
以上就是如何构建一个自己的、类似于React的虚拟DOM系统?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号