- 在README.md中添加了所有阶段的说明,包括stage00到stage09的内容 - 移除了stage05中render函数的返回值和注释,简化函数实现 - 新增stage06/index.html基础页面结构 - 实现stage06 h函数自动创建VNode功能,支持: - 类型定义VNode和VNodeChild - h函数创建虚拟节点,支持嵌套子节点 - mount函数递归挂载VNode到真实DOM - 属性设置和文本节点处理 - 提供了完整的使用示例,展示用户卡片组件的渲染
72 lines
1.6 KiB
JavaScript
72 lines
1.6 KiB
JavaScript
/**
|
||
* 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
|
||
*/
|
||
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);
|
||
}
|
||
|
||
/**
|
||
* 手动构建一个卡片 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);
|