feat(minivue): 添加stage06 h函数创建VNode功能并完善文档

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

70
stage06/index.js Normal file
View File

@@ -0,0 +1,70 @@
/**
* 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);