vue不得不会的h函数

一、定义

我们编写的代码转化为真正的dom时,首先会先转换为VNode,然后多个Vnode进行结合起来转化为VDOM,最后VDOM才渲染成真实的DOM。在 Vue.js 中,h 函数是 createElement 的别名,它是 Vue 用来创建虚拟 DOM 节点(vnode)的一个函数。这个函数在编写渲染函数(render functions)时非常有用。通过使用 h 函数,你可以更加灵活地控制应用的输出结构,而不是仅仅依赖于模板。

大部分情况下,我们都是用的模板语法开发,例如:

<template>
  <div> 
    ...
  </div>
</template>

<script setup lang="ts">
...
</script>

<style lang="less" scoped>
...
</style>

 但是,某些开发场景,我们需要借助JavaScript 完全的编程能力,所以我们就要用渲染函数来进行开发,也就是我们本文中所谈到的h函数

二、h函数的类型

// 完整参数签名
function h(
  type: string | Component,
  props?: object | null,
  children?: Children | Slot | Slots
): VNode

// 省略 props
function h(type: string | Component, children?: Children | Slot): VNode

type Children = string | number | boolean | VNode | null | Children[]

type Slot = () => Children

type Slots = { [name: string]: Slot }
  • 第一个参数type,必填,既可以是一个字符串 (用于原生元素) 也可以是一个 Vue 组件定义。
  • 第二个参数props,非必填,是要传递的 prop。一个对象,包括创建节点的属性,例如 id、class、style等,节点的事件监听也是通过 props 参数进行传递,并且以 on 开头,以 onXxx 的格式进行书写,如 onInput、onClick 等。不写的话推荐使用 null占位
  • 第三个参数children是子节点,也可以是数组。

当创建一个组件的 vnode 时,子节点必须以插槽函数进行传递。如果组件只有默认槽,可以使用单个插槽函数进行传递。否则,必须以插槽函数的对象形式来传递。为了方便阅读,当子节点不是插槽对象时,可以省略 prop 参数。

三、h函数创建原生元素

import { h } from 'vue'

// 除了 type 外,其他参数都是可选的
h('div')
h('div', { id: 'foo' })

// attribute 和 property 都可以用于 prop
// Vue 会自动选择正确的方式来分配它
h('div', { class: 'bar', innerHTML: 'hello' })

// class 与 style 可以像在模板中一样
// 用数组或对象的形式书写
h('div', { class: [foo, { bar }], style: { color: 'red' } })

// 事件监听器应以 onXxx 的形式书写
h('div', { onClick: () => {} })

// children 可以是一个字符串
h('div', { id: 'foo' }, 'hello')

// 没有 prop 时可以省略不写
h('div', 'hello')
h('div', [h('span', 'hello')])

// children 数组可以同时包含 vnode 和字符串
h('div', ['hello', h('span', 'hello')])

四、h函数创建组件:

import Foo from './Foo.vue'

// 传递 prop
h(Foo, {
  // 等价于 some-prop="hello"
  someProp: 'hello',
  // 等价于 @update="() => {}"
  onUpdate: () => {}
})

// 传递单个默认插槽
h(Foo, () => 'default slot')

// 传递具名插槽
// 注意,需要使用 `null` 来避免
// 插槽对象被当作是 prop
h(MyComponent, null, {
  default: () => 'default slot',
  foo: () => h('div', 'foo'),
  bar: () => [h('span', 'one'), h('span', 'two')]
})

五、h函数的基础使用

1.setup版本

<script>
import { h } from 'vue'
export default {
    setup() {
        return () => h('h1', { class: 'title' }, 'Hello World')
    }
}
</script>

<style lang="less" scoped>
.title {
    color: red;
}
</style>

 2.render函数版本

<script>
import { h } from 'vue'
export default {
    render() {
        return h('h1', { class: 'title' }, 'Hello World')
    }
}
</script>
<style lang="less" scoped>
.title {
    color: red;
}
</style>

效果同上。

六、h函数的实际应用

1.h函函数的v-if和v-show、v-on

<script>
import { ref, h } from 'vue'
import { ElButton } from 'element-plus'

export default {
    setup() {
        const isShow = ref(true)

        return () => h('div', [
            h(ElButton, { type: 'primary', onClick: () => isShow.value = !isShow.value }, () => isShow.value ? '隐藏' : '显示'),
            h('div', { class: { none: !isShow.value } }, '君不见黄河之水天上来(类似v-show)'),
            isShow.value ? h('div', '奔流到海不复回(类似v-if)') : null
        ])
    }
}
</script>

<style lang="less" scoped>
.none {
    display: none;
}
</style>

效果图:

 2.h函数的v-for,v-on传参

<script>
import { ref, h } from 'vue'
import { ElMessage } from 'element-plus'

export default {
    setup() {
        const list = [
            { name: '吕布', age: 36, gender: '男' },
            { name: '貂蝉', age: 28, gender: '女' },
            { name: '曹操', age: 40, gender: '男' },
            { name: '刘备', age: 35, gender: '男' },
            { name: '孙权', age: 30, gender: '男' },
        ];
        const handleClick = (item) => {
            ElMessage({
                message:`您点击的是${item.name}`,
                type: 'success'
            })
        };

        return () => h('div', list.map(
            (item) => h('div', { onClick: () => handleClick(item) }, `${item.name}今年${item.age}岁,${item.gender}`)
        ))
    }
}
</script>

效果图:

3.h函数的插槽渲染

子组件:

<script>
import { h } from 'vue'

export default {
    setup(props, { slots }) {
        return () => h('div', [
            h('div', '默认插槽开始'),
            h('div', slots.default ? slots.default() : null),
            h('div', '默认插槽结束'),
            h('hr'),
            h('div', '具名插槽special开始'),
            h('div', slots.special ? slots.special() : null),
            h('div', '具名插槽special结束'),
        ])
    }
}
</script>

父组件template版本:

<template>
  <hChildren>
    <template #default>
       <div>父传子的默认插槽内容1</div>
       <div>父传子的默认插槽内容2</div>
    </template>

    <template #special>
       <div>父传子的具名插槽special内容1</div>
       <div>父传子的具名插槽special内容2</div>
    </template>
  </hChildren>
</template>

<script setup>
import hChildren from "./hChildren.vue";
</script>

效果图:

 父组件h函数版本:

<script>
import { h } from "vue";
import hChildren from "./hChildren.vue";

export default {
  setup() {
    return () => h(hChildren, null, {
      default: [
        h('div', '父传子的默认插槽内容1'), 
        h('div', '父传子的默认插槽内容2')
      ],
      special: h('div', '父传子的具名插槽special内容1')
    })
  }
} 
</script>

效果图:

七、优势

看到这里,有些熟悉react的小伙伴会不会觉得,h函数和jsx还是有些像的。事实上,vue也是支持jsx的。那么相比jsx,h函数肯定是有属于自己的独特优势的。

h 函数的优势包括:

  • 更接近 Vue.js 的核心:h 函数是 Vue.js 的核心 API 之一,它直接与 Vue.js 的虚拟 DOM 和组件系统交互。这意味着使用 h 函数可以更直接地访问和操作 Vue.js 的内部机制。
  • 更灵活:h 函数可以用于创建任何类型的 VNode,包括自定义组件和渲染器。这使得 h 函数在创建复杂的虚拟 DOM 结构时更加灵活。
  • 更简洁:h 函数的语法比 JSX 更简洁。例如,h 函数不需要使用大括号来包裹 JavaScript 表达式,这使得代码更易读。
  • 更高效:h 函数在创建 VNode 时可以更高效地利用 Vue.js 的内部优化。例如,h 函数可以更直接地访问 Vue.js 的内部缓存,从而减少不必要的计算。

然而,h 函数也有一些缺点:

  • 学习曲线:h 函数的语法比 JSX 更复杂,对于初学者来说可能需要一些时间来学习和适应。
  • 可读性:虽然 h 函数的语法比 JSX 更简洁,但对于一些复杂的虚拟 DOM 结构,h 函数的代码可能仍然难以阅读和理解。

总的来说,h 函数和 JSX 都有各自的优点和缺点,选择使用哪一个取决于你的具体需求和偏好。如果你更熟悉 JavaScript,并且希望更直接地访问和操作 Vue.js 的内部机制,那么 h 函数可能是一个更好的选择。如果你更熟悉 JSX,并且希望更简洁的语法和更好的可读性,那么 JSX 可能是一个更好的选择。

在一些第三方的组件库里,会经常提供一些钩子函数来用于虚拟dom的重新构建与定制,诸如:element、antdesign-vue等都有类似的钩子函数。所以h函数我们就算不常用,但是也是必须要会的,还不熟悉h函数的小伙伴,抓紧行动起来,按照上线的例子亲自动手,尝试一下吧~ 

引用中提到,h函数是在Vue中使用的createElement方法,其作用是创建虚拟DOM并追踪DOM的变化。在Vue中,使用h函数可以将模板渲染成HTML。引用中提到,在Vue中,h函数用于表示模板如何渲染到HTML中的过程。因为Vue的模板最终被打包成模板字符串进行渲染,而h函数则可以通过字符串渲染到HTML中。最后,引用中给出了一个示例h函数的定义,其接受标签名、属性和子元素作为参数,返回一个虚拟DOM对象。通过虚拟DOM对象和diff算法,Vue可以追踪和更新真实DOM的变化。因此,Vue中的h函数在创建虚拟DOM和实现模板渲染到HTML的过程中起到了重要的作用。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [vue 中的h函数](https://blog.csdn.net/qq_42778001/article/details/95959531)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* [vue中 h 函数详解](https://blog.csdn.net/qq_39490750/article/details/120490473)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

零凌林

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值