h 函数
概述
Vue 提供了一个 h() 函数用于创建虚拟节点(vnodes)。h() 是 hyperscript 的简称,意思是"能生成 HTML (超文本标记语言) 的 JavaScript"。这个名字来源于许多虚拟 DOM 实现默认形成的约定。虽然一个更准确的名称应该是 createVNode(),但在需要频繁使用渲染函数时,简短的名字更具优势。
核心特点
- 高度灵活性:
h()函数可以适应各种组件和元素创建需求 - 多种参数格式:
- 支持对象形式编写
- 属性可以写成驼峰式或 kebab-case 格式(使用引号包裹)
- 事件监听器可以写成
onClick形式或对象形式
详细用法示例
h('div', null, [
h(
'div',
{
// 样式对象(驼峰式)
style: {
marginTop: '10px',
backgroundColor: '#f5f5f5'
},
// DOM 属性
props: {
value: someValue,
id: 'container'
},
// 事件监听器
on: {
input: (value) => {
this.value = value
},
change: (value) => {
handleChange(value)
},
// 也可以使用 kebab-case 格式
'custom-event': handleCustomEvent
},
// 其他特殊属性
attrs: {
'data-test': 'test-id'
},
// 类名
class: {
'active': isActive,
'disabled': isDisabled
}
},
{
// 插槽内容必须使用函数形式
default: () => [
'审核建议:',
h(ElInput, {
// 双向绑定
modelValue: var1.value,
'onUpdate:modelValue': (val: string) => {
var1.value = val
},
// 其他属性
placeholder: '请输入',
type: 'textarea',
minRows: 4,
maxlength: 1000,
showWordLimit: true,
// 原生事件
onClick: handleClick,
// 自定义指令
directives: [
{
name: 'focus',
value: true
}
]
})
]
}
)
])
插槽处理最佳实践
常见错误:"Non-function value encountered for default slot."
- 性能优化:Vue 3 推荐使用函数式插槽以获得更好的性能
- 显式声明:即使只是默认插槽,也需要显式写出
default: () => ...的形式 - 多插槽处理:当组件有多个插槽时,需要为每个插槽提供函数
// 错误写法 - 直接传递内容
return h(Component, { props }, content)
// 正确写法 - 默认插槽
return h(Component, { props }, {
default: () => content
})
// 多个插槽
return h(Component, { props }, {
default: () => [content1, content2],
header: () => h('div', 'Header'),
footer: () => h('div', 'Footer')
})
进阶技巧
动态组件:
h(resolveComponent(componentName), { ...props })递归组件:
h('div', { key: node.id }, [ node.children ? node.children.map(child => h(recursiveComponent, { node: child })) : null ])JSX 替代:
const vnode = <div id="container">Hello</div>指令使用:
h('div', { directives: [ { name: 'my-directive', value: someValue, arg: 'arg', modifiers: { modifier: true } } ] })
性能优化
- 避免不必要的重新渲染:为列表项提供稳定的
key - 合理使用函数式组件:对于无状态的展示组件
- 缓存静态内容:将不变化的 vnodes 缓存起来
- 合理使用片段:减少不必要的包装元素
版本差异说明
Vue 2 vs Vue 3:
- Vue 2 使用
createElement而非h - Vue 3 的插槽必须使用函数形式
- Vue 3 对属性处理更加严格
- Vue 2 使用
Composition API:
import { h } from 'vue' export default { setup() { return () => h('div', 'Hello') } }