/** * Stage 05:虚拟 DOM 初体验 * 用纯 JavaScript 对象描述 DOM 树,并编写递归渲染函数 */ /** * @typedef {Object} VNode * @property {string} type - 元素标签或 'text' * @property {Object} [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);