返回 导航

Vue.js

hangge.com

Vue.js 3 - render函数、h函数、JSX语法使用详解

作者:hangge | 2026-08-25 09:14
    在 Vue.js 3 中,除了我们熟悉的模板语法(template),还有一种更底层、更灵活的方式来构建界面:render 函数。配合 h 函数和 JSX 语法,开发者可以像写 JavaScript 一样精确地控制虚拟 DOM 的生成。
提示: 实际上,我们在模板中编写的 HTML 最终也会通过渲染函数生成对应的 VNode。如果想充分利用 JavaScript 编程的灵活性,那么可以在 render 函数中使用 createVNode 函数生成对应的 VNode。多个 VNode 组合在一起,就形成了一棵树形结构,即虚拟 DOM

1,h 函数介绍

(1)在 Vue.js 3 中,我们可以在 render 函数中使用 createVNode 函数生成对应的 VNode。为了简便,Vue.js 3createVNode 函数简化为 h 函数,所以 h 函数是一个用于创建 VNode 的函数。

(2)h 函数包含三个参数:
  • 参数一(必需):可以接收一个 HTML 标签名、组件、异步组件或函数式组件。 支持类型:{String|Object|Function}tag。 
  • 参数二(可选):一个与 attributeprop 和事件相对应的对象,会在模板中用到。 支持类型:{Object}props。 
  • 参数三(可选):接收子 VNodes,使用 h 函数构建,也可以使用字符串获取“文本 VNode”或有插槽的对象。支持类型:{String|Array|Object}children。 
注意:如果没有参数二(props),那么通常可以将 children 作为参数二传入。为了避免产生歧义,可以将 null 作为参数二传入,将 children 作为参数三传入。

2,h 函数基本用法

(1)h 函数可以在 render 函数和 setup 函数中使用。下面先演示在 render 函数中使用:
<script>
import { h } from 'vue'; // 导入h函数
export default {
    render() { // render函数
        return h("h4", { class: "title" }, "欢迎访问hangge.com")
    }
}
</script>
<style scoped>
.title {
    text-decoration: underline;
}
</style>
<template>
    <h4 class="title">欢迎访问hangge.com</h4>
</template>
<script>
export default {
}
</script>
<style scoped>
.title {
    text-decoration: underline;
}
</style>

(2)运行效果如下:

3,render 函数实现计数器

(1)下面使用 render 函数实现一个复杂一些计数器样例:
  • 首先在 data 中定义 counter 变量。
  • 接着在 render 函数中使用 h 函数构建一个 <div> 元素,其中包含一个 <h4> 元素和两个 <button> 元素的计数器案例页面,在 render 函数中也可以直接通过 this 访问到组件的实例。
<script>
import { h } from 'vue'; // 导入h函数
export default {
    data() {
        return { counter: 0 }
    },
    render() {
        // div标签中包含:一个h4元素和2个button元素  
        return h("div", { class: "render-counter" }, [
            h("h4", null, `当前计数: ${this.counter}`),
            h("button", {
                onClick: () => this.counter++
            }, "+1"),
            h("button", {
                onClick: () => this.counter--
            }, "-1"),
        ])
    }
}
</script>

(2)运行效果如下:

4,setup 函数实现计数器

(1)上面样例使用 render 函数实现的计数器用的是 OptionsAPI 语法,如果想要使用 Composition API 语法实现计数器,那么可以在 setup 函数中编写代码。 代码如下:
  • 首先使用 ref 定义响应式变量。
  • 接着在 setup 函数中返回 render 函数。
<script>
import { ref, h } from 'vue';
export default {
    setup() {
        const counter = ref(0); // 定义变量
        // setup函数返回一个render函数, 该函数可以直接使用在同一作用域中声明的响应式状态
        return () => {
            return h("div", { class: "app" }, [
                h("h2", null, `当前计数: ${counter.value}`),
                h("button", {
                    onClick: () => counter.value++
                }, "+1"),
                h("button", {
                    onClick: () => counter.value--
                }, "-1"),
            ])
        }
    }
}
</script>

(2)运行在浏览器中的效果和上面的案例一样:

5,在 render 函数中实现插槽

(1)在使用 render 函数构建页面时,如果组件中需要添加插槽,那么可以使用 this.$slots 来实现。首先我们创建一个 HelloWorld.vue 组件代码如下,可以看到,在 render 函数中使用 this.$slots.default 判断父组件是否传递了 default 默认插函数:
  • 如果有传递,那么调用 default 默认插槽函数,并传递一个对象给该函数作为参数。 
  • 如果父组件没有传递 default 默认插槽函数,那么使用插槽的默认值,即显示一个 <span> 元素。 
<script>
import { h } from "vue";
export default {
  render() {
    // 等价于:  
    // <div> 
    //     <h4>HelloWorld组件</h4> 
    //     <slot name="default"> 
    //         <span>插槽默认值</span> 
    //     </slot> 
    // </div>
    return h("div", { class: "hello-world" }, [
      h("h4", null, "HelloWorld组件"),
      // 定了一个默认的插槽
      this.$slots.default ? this.$slots.default({ name: "hangge" }) : h("span", null, "插槽默认值")
    ])
  }
}
</script>
<style scoped>
.hello-world {
  border: 1px solid #ddd;
  margin: 8px;
}
</style>

(2)下面是使用这个 HelloWorld.vue 组件的样例代码,同样使用 render 函数进行渲染:
  • 首先,在该组件中导入 HelloWorld.vue 组件。
  • 接着,在 render 函数中的 h 函数里使用该组件。 
  • 最后,为该组件传递 default 默认插槽函数,该函数返回的内容会替换掉 HelloWorld.vue 组件中定义的默认插槽,该函数的 props 参数中可以接收 HelloWorld.vue 组件中的插槽传递过来的参数。 
<script>
import { h } from 'vue';
import HelloWorld from './HelloWorld.vue'; // 1.导入HelloWorld组件
export default {
    // 等价于:
    // <div class="render-slot">
    //   RenderSlot组件
    //   <HelloWorld>
    //      <template v-slot:default="slotProps">
    //          <span>传入HelloWorld插槽中的内容。插槽props的name: {{slotProps.name}}</span>
    //      </template>
    //   </HelloWorld>  
    // </div>
    render() {
        return h("div", { class: "render-slot" }, [
            'RenderSlot组件',
            // 2.使用HelloWorld组件   
            h(HelloWorld, null, {
                // 3.使用HelloWorld组件定义的默认插槽。props是:插槽props
                default: props => h("span", null,
                    `传入HelloWorld插槽中的内容。插槽props的name:${props.name}`)
            })
        ])
    }
}
</script>
<style scoped>
.render-slot {
    border: 1px solid #ddd;
    margin: 8px;
}
</style>

附:组件的 JSX 语法

1,基本介绍

(1)在 Vue.js 3 中,虽然可以使用 h 函数构建组件的布局,但是其编写效率非常低,并且不利于阅读和后期维护。 

(2)为了解决这个问题,Vue.js 3 支持使用 JSX 语法编写组件,该语法更接近于模板语法。JSX 语法实际上是一种允许在 JavaScript 中编写 HTML 元素的语法糖。
提示:为了将 JSX 语法编写的模板转换成 h 函数的写法,Vue.js 3 底层需要借助 Babel 插件(@yue/babel-plugin-jsx)。该插件已经被集成并安装在 Vue CLI 脚手架中,因此可以在 Vue.js3 的单文件组件中直接编写 JSX 语法。 

2,计数器样例

(1)下面样例在 render 函数中使用 JSX 语法编写计数器案例的模板。可以看到,相比于 h 函数编写的计数器案例,JSX 语法更接近于模板的语法,也便于阅读和后期维护。
提示:在编写 JSX 语法时, 一般使用一个括号来包裹 JSX 编写的模板,这样既可以提高代码的可读性,也能使代码更美观。
<script lang="jsx">
export default {
    data() {
        return { counter: 0 }
    },
    render() {
        // 下面的方法也可以写到 methods 选项中
        const increment = () => this.counter++;
        const decrement = () => this.counter--;
        return (
            <div class="app" style="border:1px solid #ddd;padding:4px">
                <h4>当前计数: {this.counter}</h4>
                <button onClick={increment}>+1</button>
                <button onClick={decrement}>-1</button>
            </div>
        )
    }
}
</script>

(2)运行效果如下:

3,插槽样例

(1)JSX 语法不仅支持编写 HTML 元素,还支持编写 Vue.js 组件。首先我们新建一个名为 HelloWorld.vue 的组件,代码如下:
  • 首先在该组件的 render 函数中使用 JSX 语法编写模板。
  • 然后在该组件中定义个默认的插槽。
<script lang="jsx">
export default {
  render() {
    return (
      <div style="border:1px solid #ddd;margin:8px">
        <div>HelloWorld组件</div>
        {/**这里编写的是注释:定义default插槽*/}
        {this.$slots.default ? this.$slots.default() : <span>插槽默认内容</span>}
      </div>
    )
  }
}
</script>

(2)接着我们在父组件中导入 HellWorld.vue 组件,然后在 render 函数的 JSX 模板中使用该组件。
<script lang="jsx">
import HelloWorld from './HelloWorld.vue';
export default {
    render() {
        return (
            <HelloWorld />
        )
    }
}
</script>

(3)运行效果如下:
评论

全部评论(0)

回到顶部