💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
>[success] # h 函数 1. 在通常情况下我们使用的是模板的形式进行编程,把用户在`<template></template>`标签中写的类似于原生HTML的内容进行编译,把原生`HTML`的内容找出来,再把非原生`HTML`找出来,经过一系列的逻辑处理生成渲染函数,也就是`render`函数的这一段过程称之为模板编译过程。 * **Vue在生成真实的DOM之前,会将我们的节点转换成VNode,而VNode组合在一起形成一颗树结构,就是虚拟DOM** 2. `vue `提供了`h() `函数是一个**用于创建 vnode 的一个函数**,其实更准备的命名是 `createVNode() `函数,但是为了简便在Vue将之简化为` h()` 函数;其实可以发现[h ](https://github1s.com/vuejs/core/blob/060c5f1d0ae999cd8c8fb965e8526ffab17ac2d1/packages/runtime-core/src/h.ts)函数内部就是一个包裹[createVNode ]([https://github.com/vuejs/vue-next/blob/060c5f1d0ae999cd8c8fb965e8526ffab17ac2d1/packages/runtime-core/src/vnode.ts#L326](https://github.com/vuejs/vue-next/blob/060c5f1d0ae999cd8c8fb965e8526ffab17ac2d1/packages/runtime-core/src/vnode.ts#L326))函数 ![](https://img.kancloud.cn/02/2d/022d37d9bd0247949418b67389c338ad_668x471.png) * 二者的区别 ~~~ h('strong', 'Foo') ~~~ 对于 createVNode,必须执行以下操作: ~~~ createVNode('strong', null, 'Foo') ~~~ 3. `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 } ~~~ * 创建原生元素 ~~~ 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')]) ~~~ * 如果是使用组件,**子节点必须以插槽函数进行传递。如果组件只有默认槽,可以使用单个插槽函数进行传递。否则,必须以插槽函数的对象形式来传递** ~~~ 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')] }) ~~~ >[info] ## 案例 1. 使用`h` 函数 可以在` Option API` 或者 `Composition API` 中使用,完成下面视图效果案例 ![](https://img.kancloud.cn/13/5d/135d5c72bbe495a6fafb359669ffdd6d_932x810.png) >[danger] ##### Option Api 案例 * 可以发现使用 h 函数后完全不在需要`template `模板了 * 在使用事件使用不能在使用`@`缩写了 ,应该使用全程写法 ~~~ <script> import { h } from 'vue' import Home from "./Home.vue" export default { data() { return { counter: 0 } }, render() { return h("div", { className: "app" }, [ h("h2", null, `当前计数: ${this.counter}`), h("button", { onClick: this.increment }, "+1"), h("button", { onClick: this.decrement }, "-1"), h(Home) ]) }, methods: { increment() { this.counter++ }, decrement() { this.counter-- } } } </script> <style scoped> </style> ~~~ >[danger] ##### Composition API 案例 * 不在使用`this` * `ref` 不在`h` 函数使用时候需要自己进行 `.value` ~~~ <script> import { h, ref } from 'vue' export default { setup() { const counter = ref(0) const increment = () => { counter.value++ } const decrement = () => { counter.value-- } return () => [ h('div', { class: 'info', style: { color: 'red' } }, '你好'), h('div', [ h('h2', { className: 'title' }, '我1是标题'), h('p', { className: 'content' }, '我是内容, 哈哈哈'), ]), h('div', [ h( 'button', { onClick: increment }, // 显示的内容字符串 '加' ), h('span', `${counter.value}`), h( 'button', { onClick: decrement }, // 显示的内容字符串 '减' ), ]), ] }, } </script> ~~~ >[danger] ##### 使用setup 语法糖 * 使用 `setup` 语法糖需要在`template`使用定义好的`render `函数 ~~~ <template> <render /> </template> <script setup> import { h, ref } from 'vue' const counter = ref(0) const increment = () => { counter.value++ } const decrement = () => { counter.value-- } const render = () => [ h('div', { class: 'info', style: { color: 'red' } }, '你好'), h('div', [ h('h2', { className: 'title' }, '我1是标题'), h('p', { className: 'content' }, '我是内容, 哈哈哈'), ]), h('div', [ h( 'button', { onClick: increment }, // 显示的内容字符串 '加' ), h('span', `${counter.value}`), h( 'button', { onClick: decrement }, // 显示的内容字符串 '减' ), ]), ] </script> ~~~ >[info] ## 综合案例 * v-for 使用map 来代替了 * v-if 使用三元做判断 整体完全都是按照js 思路去书写 ![](https://img.kancloud.cn/fb/e9/fbe9398ffdd105d50a2272899943994f_234x158.png) ~~~ <script> import { h, ref } from 'vue' export default { setup() { const infos = ref([ { name: 'w', age: '22' }, { name: 'zz', age: '12' }, { name: 'rr', age: '19' }, ]) const show = ref(false) const findInfoByName = (name) => { return infos.value.find((info) => info.name === name) } return () => [ h( 'div', // 用map 替代了v-for infos.value.map(({ name, age }, index) => { return h('div', { key: index }, `${name}--${age}`) }) ), h( 'button', { onClick: () => { show.value = !show.value }, }, '展示/隐藏' ), // 三元代替了 v-if [show.value ? h('div', `${findInfoByName('w').name}`) : ''], ] }, } </script> ~~~ >[info] ## 官网地址 https://cn.vuejs.org/guide/extras/render-function.html#jsx-tsx https://cn.vuejs.org/api/render-function.html#h