Files
minivue/stage05/index.js
zzy 845789cd60 feat(stages): 添加性能测试和虚拟DOM实现阶段
添加stage04-perf用于真实DOM操作性能对比,包含三个不同的更新策略:
- innerHTML全量更新
- createElement全量重建
- 精确单节点更新

添加stage04用于模拟异步接口数据获取后的渲染演示

添加stage05实现虚拟DOM基础功能,提供VNode对象描述DOM树结构和递
归渲染函数
2026-05-02 12:36:53 +08:00

74 lines
1.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Stage 05虚拟 DOM 初体验
* 用纯 JavaScript 对象描述 DOM 树,并编写递归渲染函数
*/
/**
* @typedef {Object} VNode
* @property {string} type - 元素标签或 'text'
* @property {Object<string, string>} [props] - 属性
* @property {(VNode|string)[]} [children] - 子节点
* @property {string} [value] - 文本内容(仅 type='text' 时使用)
*/
/**
* 渲染 VNode 树为真实 DOM 并挂载到容器
* @param {VNode} vnode
* @param {HTMLElement} container
* @returns {HTMLElement | Text} 挂载的真实 DOM
*/
function render(vnode, container) {
let el;
if (vnode.type === "text") {
el = document.createTextNode(vnode.value);
} else {
el = document.createElement(vnode.type);
// 设置属性
if (vnode.props) {
for (const key in vnode.props) {
el.setAttribute(key, vnode.props[key]);
}
}
// 递归渲染子节点
if (vnode.children) {
vnode.children.forEach((child) => {
// 子节点可能是字符串 (自动转为文本 VNode)
if (typeof child === "string") {
render({ type: "text", value: child }, el);
} else {
render(child, el);
}
});
}
}
container.appendChild(el);
return el;
}
/**
* 手动构建一个卡片 VNode
* @type {VNode}
*/
const userNode = {
type: "div",
props: { class: "user-card" },
children: [
{
type: "h2",
children: [{ type: "text", value: "姓名: 小明" }],
},
{
type: "p",
children: [{ type: "text", value: "年龄: 18" }],
},
{
type: "span",
children: [{ type: "text", value: "学生" }],
},
],
};
const root = document.getElementById("minivue");
render(userNode, root);