Files
minivue/stage06/index.js
zzy 35b4b22093 feat(minivue): 添加stage06 h函数创建VNode功能并完善文档
- 在README.md中添加了所有阶段的说明,包括stage00到stage09的内容
- 移除了stage05中render函数的返回值和注释,简化函数实现
- 新增stage06/index.html基础页面结构
- 实现stage06 h函数自动创建VNode功能,支持:
  - 类型定义VNode和VNodeChild
  - h函数创建虚拟节点,支持嵌套子节点
  - mount函数递归挂载VNode到真实DOM
  - 属性设置和文本节点处理
- 提供了完整的使用示例,展示用户卡片组件的渲染
2026-05-02 13:50:16 +08:00

71 lines
1.6 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 06用 h 函数自动创建 VNode
* 从“手动拼对象”到“用函数生成”,与 Vue 的 createElement 原理一致
*/
/**
* @typedef {string|number|VNode} VNodeChild
*/
/**
* @typedef {Object} VNode
* @property {string} type
* @property {Object<string, any>} [props]
* @property {VNodeChild[]} [children]
* @property {HTMLElement | null} [el]
*/
/**
* 创建虚拟节点(VNode)
* @param {string} type - 标签名,如 'div'
* @param {Object<string, any> | null} props - 属性或 null
* @param {...(string|VNode)} children - 子节点,字符串会自动转为文本节点
* @returns {VNode}
*/
function h(type, props, ...children) {
const normalizedChildren = children.flat(Infinity);
return {
type,
props: props || {},
children: normalizedChildren,
};
}
/**
* 将 VNode 挂载到真实 DOM递归
* @param {VNode} vnode
* @param {HTMLElement} container
*/
function mount(vnode, container) {
const 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) => {
if (typeof child === "string" || typeof child === "number") {
el.appendChild(document.createTextNode(String(child)));
} else {
mount(child, el);
}
});
}
container.appendChild(el);
}
const root = document.getElementById("minivue");
const vnode = h(
"div",
{ class: "user-card" },
h("h2", null, "姓名: 小明"),
h("p", null, "年龄: 18"),
h("span", null, "学生"),
);
mount(vnode, root);