在 Vue(特别是 Vue 3)中,h 函数是 创建虚拟 DOM 节点(VNode) 的核心工具。它的名字来源于 "hyperscript" ------ 一种用 JavaScript 编写 HTML 结构的编程范式。
一、h 函数的作用
- 创建 VNode(虚拟节点):描述一个 DOM 元素或组件的结构、属性和子内容。
- 替代模板语法 :当你无法使用
<template>(如动态生成 UI、高阶组件、函数式组件等),可用h()编程式构建 UI。 - 底层渲染机制 :Vue 的模板最终会被编译成
h()调用,因此h是 Vue 渲染系统的基石。
✅ 简单说:h('div', { class: 'foo' }, 'Hello') ≈ <div class="foo">Hello</div>
二、基本语法
ts
h(type, props?, children?)
参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
type |
`string | Component` |
props |
`object | null`(可选) |
children |
`string | number |
⚠️ 注意:props 可省略。若第二个参数是数组或字符串,Vue 会自动将其视为 children,props 设为 null。
三、常见使用示例
1. 创建原生 HTML 元素
js
import { h } from 'vue'
// 最简形式
h('div')
// 带属性
h('div', { id: 'app', class: 'container' })
// 带子节点
h('div', { class: 'title' }, 'Hello World')
// 子节点为数组
h('ul', [
h('li', 'Item 1'),
h('li', 'Item 2')
])
2. 创建组件
js
import MyComponent from './MyComponent.vue'
// 传递 prop
h(MyComponent, { message: 'Hi!' })
// 传递默认插槽
h(MyComponent, () => 'Default slot content')
// 传递具名插槽(注意:需显式传 null 作为 props)
h(MyComponent, null, {
default: () => 'Default',
header: () => h('h1', 'Header')
})
3. 事件与样式
js
h('button', {
class: ['btn', { primary: true }],
style: { color: 'red', fontSize: '16px' },
onClick: () => alert('Clicked!')
}, 'Click me')
4. 在 setup() 中返回渲染函数(组合式 API)
js
import { h, ref } from 'vue'
import ChildComp from './Child.vue'
export default {
setup() {
const count = ref(0)
const increment = () => count.value++
// 返回一个渲染函数
return () => h('div', [
h('p', `Count: ${count.value}`),
h('button', { onClick: increment }, '+1'),
h(ChildComp)
])
}
}
🔔 在 <script setup> 中,通常通过 <component :is="renderFn" /> 使用。
四、高级特性
✅ 自动归一化(Normalization)
class/style支持对象、数组格式(如{ active: true }或['a', 'b'])- 事件监听器以
onXxx形式书写(如onClick→click事件) children自动扁平化嵌套数组,并过滤null/undefined
✅ 多参数灵活调用
js
h('div', 'text') // type + children
h('div', { id: 'x' }) // type + props
h('div', { id: 'x' }, 'text') // type + props + children
h('div', {}, 'a', 'b') // children 合并为 ['a', 'b']
五、与模板的关系
Vue 模板是语法糖,最终会被编译为 h() 调用:
html
<!-- 模板 -->
<div class="box" @click="handle">{{ msg }}</div>
⬇️ 编译后 ≈
js
h('div', {
class: 'box',
onClick: handle
}, ctx.msg)
六、何时使用 h()?
| 场景 | 说明 |
|---|---|
| ✅ 动态标签 | 如根据 level 渲染 <h1> ~ <h6> |
| ✅ 高阶组件 / 函数式组件 | 包装或增强其他组件 |
| ✅ 复杂条件渲染 | 模板中 v-if 嵌套过深时 |
| ✅ 表格/列表自定义渲染 | 如 Naive UI / Element Plus 的 render-cell |
| ❌ 简单 UI | 优先用 <template>,更易读维护 |
总结
h是 Vue 3 中创建 VNode 的标准方式。- 它提供比模板更强的程序控制力 ,适合动态性高、逻辑复杂的场景。
- 虽然灵活,但不推荐滥用------简单 UI 仍应使用模板。
📌 记住:模板 → 编译 → **h()** → VNode → 真实 DOM
如需深入,可查看 Vue 官方文档:渲染函数 - Vue.js