h函数是Vue.js手动创建VNode的核心工具,支持三种重载形式:h(type)、h(type, props)、h(type, props, children),可实现动态子节点、嵌套控制、内置组件协同及精细化DOM生成。

Vue.js 中的 h 函数是手动创建 VNode 的核心工具,它不仅是渲染函数(render function)的基石,也是编写高阶组件、自定义指令底层逻辑、JSX 编译目标以及跨平台渲染的关键接口。掌握其高级用法,能让你更精准地控制 DOM 生成过程,写出更高效、更灵活的渲染逻辑。
理解 h 函数的参数结构与重载规则
h 不是一个固定三参数的函数,而是支持多种签名的重载函数。Vue 3 的 h 主要有以下三种常用形式:
-
h(type):仅传入组件或 HTML 标签名,生成空节点(如h('div')) -
h(type, props):传入属性对象,不带子节点(如h('button', { disabled: true })) -
h(type, props, children):完整形式,支持嵌套结构(children 可为字符串、数组、其他 VNode 或函数)
注意:props 中的 class 和 style 支持对象/数组语法;事件需以 onXXX 形式绑定(如 onClick),且值必须是函数(可包裹箭头函数或方法引用)。
用函数式子节点实现动态内容与作用域插槽模拟
当 children 是一个函数时,Vue 会将其作为“渲染函数”调用,并将当前上下文(如 props、slots)注入。这在封装可复用逻辑时非常实用:
立即学习“前端免费学习笔记(深入)”;
- 可用于实现类似
<slot>的运行时内容分发,比如写一个RenderlessToggle组件,只管理状态,把渲染完全交给用户传入的函数 - 结合
setup()中的useSlots(),可在 render 函数中手动调用具名插槽并透传 props - 示例:
h('div', {}, () => [h('span', '动态生成'), h(MyComp, { msg: 'hello' })])
嵌套 VNode 与组件通信的精细化控制
通过手写 VNode,你可以绕过模板语法限制,直接构造含复杂逻辑的节点树。例如:
- 条件性插入节点:用三元表达式或 if-else 构造 children 数组,避免
v-if的响应式开销(适用于静态分支) - 批量克隆/修改子节点:遍历
slots.default?.()返回的 VNode 数组,对每个节点的props增加统一 class 或监听器 - 向子组件注入响应式数据:在
h(ChildComp, { ...props }, children)中,把computed或ref直接作为 prop 传入,子组件可 reactive 地消费
与 Teleport、Suspense 等内置组件协同工作
h 可直接创建 Vue 内置组件实例,从而在 render 函数中精确调度:
-
h(Teleport, { to: '#modal-root' }, slots.default?.())—— 手动指定挂载点,适合模态框、Tooltip 等脱离文档流的场景 -
h(Suspense, {}, { default: () => [...], fallback: () => h('div', 'loading...') })—— 显式定义异步组件的加载态与完成态 - 配合
defineAsyncComponent使用时,h能让你在加载失败时返回自定义错误节点,而非抛出异常
不复杂但容易忽略:h 函数返回的是普通 JS 对象(VNode),不是真实 DOM;它的结构受 Vue 内部规范约束,不建议手动修改 key、type 等保留字段;调试时可用 console.dir(vnode) 查看其 shape,重点关注 type、props、children、key 和 shapeFlag 字段。










