Code前端首页关于Code前端联系我们

Vue3写组件只会用template?h函数到底什么时候用,又该怎么用好?

terry 1小时前 阅读数 16 #Vue

很多Vue开发者对h函数的误解从哪来?

第一次接触Vue3官方文档,看到「渲染函数&h函数」部分时,不少人心里都会犯嘀咕:这不就是JSX换了个语法糖包装吗?而且看起来比写template啰嗦一百倍,要手动嵌套元素、绑定属性、处理事件,甚至连v-if/v-for这种基础指令都得自己写条件循环语句,放着好好的HTML式、语义化、自带Vue指令加持的template不用,干嘛要学h函数? 其实这种“先入为主的嫌弃”很正常,但完全低估了h函数的作用——它是Vue组件渲染的底层核心,template里写的每一行最终都会被Vue的编译器转化为带h函数的渲染函数,本质上是Vue给开发者提供的一套“跳开编译器约束,直接和虚拟DOM对话”的工具,而且在某些特定场景下,用h函数比用template开发效率更高、代码更灵活、性能也可能更好。

先搞懂:h函数到底是什么,它的基本语法和规则?

既然是和虚拟DOM对话的工具,那h函数的作用就是快速创建虚拟DOM节点(VNode),然后Vue会把这些VNode渲染成真实的DOM元素。

h函数的完整参数

在Vue3中,h函数是从vue核心库导出的全局或局部函数,它的语法结构其实很简单,有3个主要参数:

import { h } from 'vue'
// 参数1:必填,标签名/组件名/函数式组件/异步组件
// 参数2:可选,对象类型,包含属性、事件、class、style等
// 参数3:可选,字符串/数组/对象/插槽内容,代表子节点
h(参数1, 参数2, 参数3)

这三个参数每个都有很多细节要注意,不能随便写,否则会渲染不出想要的效果。

参数1的常见写法

最常用的是字符串,代表HTML原生标签,比如h('div')、h('input');如果是写在组件内部的子组件,可以直接传组件的引用,比如import MyBtn from './MyBtn.vue',然后h(MyBtn);也可以传异步组件或者函数式组件,后面进阶场景会讲。

参数2的分类绑定

这个参数是新手最容易混淆的,因为Vue把原生DOM属性、Vue组件属性、事件、class、style、ref、v-model等效逻辑、插槽等都整合到了这一个对象里,需要按规则分类:

  • 原生属性/元素属性:直接写键值对,比如h('input', { type: 'text', placeholder: '请输入内容' })
  • class和style:支持字符串、数组、对象三种写法,和template里的v-bind:class/v-bind:style完全一致,比如h('div', { class: ['container', { active: isActive }], style: { color: 'red', fontSize: '16px' } })
  • 事件监听:要加'on'前缀,首字母大写事件名(虽然有时候小写也能生效,但官方推荐大写,和template里的v-on:@事件对应),比如h('button', { onClick: handleClick });如果是组件的自定义事件,也是一样加'on'前缀,比如h(MyBtn, { onMyEvent: handleMyEvent })
  • v-model等效逻辑:因为h函数里不能直接写v-model,需要拆成modelValue属性和onUpdate:modelValue事件,比如h('input', { modelValue: inputVal, 'onUpdate:modelValue': (val) => inputVal.value = val })
  • ref绑定:直接传ref的响应式引用或者回调函数,和template里的ref属性作用一样,比如const divRef = ref(null),h('div', { ref: divRef })
  • 其他Vue特殊属性:比如key、key、is、v-slots等效逻辑(可以直接在参数3里写,但如果参数3比较复杂或者需要命名插槽,也可以放在参数2的'slots'属性里)

    参数3的子节点组织

    参数3可以是字符串,代表纯文本内容;也可以是数组,里面放多个h函数生成的VNode或者纯文本;还可以是对象,代表作用域插槽的内容,如果参数2是空对象或者不需要参数2,可以省略它直接传参数3,但要注意不能连续省略两个参数,比如h('div', null, '纯文本')、h('div', ['子元素1', h('span', '子元素2')])。

新手必做的练习:把简单的template代码转化为h函数

光说规则太干,我们可以拿几个最常用的template片段来练手,看看编译器是怎么处理的,也能更快掌握h函数的写法。

第一个练习:基础嵌套+class/style+事件

比如这段template代码:

<template>
  <div class="card" :style="{ backgroundColor: isDark ? '#333' : '#fff' }">
    <h2 class="card-title" @click="toggleTitle">{{ title }}</h2>
    <p v-if="showContent">{{ content }}</p>
    <ul v-else>
      <li v-for="item in list" :key="item.id">{{ item.text }}</li>
    </ul>
  </div>
</template>
<script setup>
import { ref, computed } from 'vue'
const isDark = ref(true)
const title = ref('基础嵌套练习')
const showContent = ref(true)
const content = ref('这是卡片的内容')
const list = ref([
  { id: 1, text: '列表项1' },
  { id: 2, text: '列表项2' },
  { id: 3, text: '列表项3' }
])
const toggleTitle = () => {value = title.value === '基础嵌套练习' ? '点击切换后的标题' : '基础嵌套练习'
}
</script>

我们可以一步步把它转化为h函数:

  1. 首先导入h函数,去掉template标签,把逻辑改成普通的Vue3组件(因为Vue3也支持渲染函数组件,不过新手可以先从