h() 是生成 VNode 的工厂函数,render() 将其递归转为真实 DOM:h() 返回含 type/props/children 的对象,render() 创建元素、设置属性、递归挂载子节点。

h() 函数不是魔法,它本质就是一个工厂函数,负责生成描述 DOM 结构的 JavaScript 对象(VNode)。手写一个简化版渲染器,能帮你跳过框架黑盒,看清“模板 → VNode → 真实 DOM”这条链路里真正发生的事。
核心目标:用 h() 创建 VNode,再把它挂载到真实 DOM
一个最小可行的渲染器只需两部分:一是能创建 VNode 的 h(),二是能把 VNode 变成真实节点并插入页面的 render()。不需要响应式、diff 算法或组件系统——先跑通最基础的一环。
- h() 只返回一个普通对象:包含 type(标签名/组件)、props(属性/事件)、children(子节点)三个关键字段
- render() 负责递归创建真实 DOM:遇到字符串就创建文本节点;遇到 VNode 就 createElement、设置属性、遍历 children 并 appendChild
- 不处理事件绑定细节(比如 on:click → addEventListener),先用原生 onclick 属性模拟
手写 h():参数灵活但结构固定
官方 h() 支持多种参数组合,但底层始终归一为 { type, props, children }。简化版可只支持最清晰的三参数形式:
- 第一个参数 type:必须,可以是 'div'、'span' 这类字符串,也可以是组件定义(暂不实现组件,先用字符串)
- 第二个参数 props:可选,传 null 或 {} 都行;class、style、onclick 等直接作为对象属性写入
- 第三个参数 children:可选,支持字符串、数组(含多个 VNode 或字符串)、甚至 null
例如:h('div', { class: 'box', onclick: () => alert('hi') }, ['Hello', h('b', null, 'World')]) —— 这个调用会生成一个带类名、事件和混合子内容的 VNode 对象。
立即学习“前端免费学习笔记(深入)”;
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
手写 render():把 VNode 树变成真实 DOM 树
render(vnode, container) 的逻辑很直白:
- 若 vnode 是字符串,
document.createTextNode(vnode) - 若 vnode 是对象,
document.createElement(vnode.type),再遍历 props 设置属性(如el.className = props.class,el.onclick = props.onclick) - 最后对 children 递归调用 render,把每个子节点 append 到当前 el 上
注意:这里不区分 text node 和 element node 的 diff,每次都是全量替换——这正是“简化版”的意义:先验证流程,再叠加优化。
组合起来跑一个例子
写完 h 和 render 后,就能这样用:
const vNode = h('div', { id: 'app' }, [
h('h1', null, '计数器'),
h('p', null, `当前值:${0}`),
h('button', { onclick: () => console.log('clicked') }, '点我')
])
render(vNode, document.body)
执行后,页面 body 里就会出现对应的真实 DOM。没有编译、没有模板字符串、没有依赖,只有 JS 对象和原生 DOM API 的直接映射——这就是 h() 最原始也最本质的作用。

















