vue中的h渲染函数

渲染函数

官网地址:
https://cn.vuejs.org/guide/extras/render-function

推荐先看一下vue官方的渲染机制:

https://cn.vuejs.org/guide/extras/rendering-mechanism.html

一、渲染函数的核心概念与应用场景

1.1 为什么需要渲染函数?

  • 模板的局限性:虽然模板语法直观易用,但在处理高度动态的渲染逻辑(如根据数据结构动态生成组件树)或封装可复用的高级组件(如表单生成器、可视化编辑器)时,模板的灵活性会受限。
  • 性能与底层控制:渲染函数直接操作虚拟 DOM(VNode),避免模板编译开销,适合高频更新场景,同时能实现模板无法完成的底层 DOM 控制。

1.2 渲染函数与 Vue 版本的关系

  • Vue 2:h函数是Vue.extend和模板编译的底层实现,通过render选项使用。
  • Vue 3:h函数是 Composition API 中render函数的基础,配合setup函数使用更灵活,且优化了 VNode 结构和 diff 算法。

二、深入理解 h 函数:创建虚拟节点的核心工具

2.1 创建Vnodes

vue提供了一个h()函数创建vnodes

js 复制代码
import {h} from 'vue'

const vnode = h(
  type,           // 必选:节点类型(HTML标签/组件/函数)
  [propsOrChildren],  // 可选:属性对象或子节点
  [children]      // 可选:子节点(当第二个参数为属性时)
)
  1. type
    • HTML 标签:h('div')、h('button')
    • 组件对象:import MyComponent from './MyComponent.vue'; h(MyComponent)
    • 函数:返回 VNode 的函数,如h(() => h('span', '动态节点'))
  2. propsOrChildren
    • 属性对象:包含 DOM 特性、组件 Prop、事件等,如{ id: 'box', class: 'container' }
    • 子节点(字符串 / 数组):当省略属性时,第二个参数为子节点,如h('div', '文本内容')
  3. children
    • 支持嵌套 VNode 数组,如 h('div', null, [h('p', '段落1'), h('p', '段落2')])

2. 2 h函数的使用

  • 除了类型必填以外,其他的参数都是可选的

    js 复制代码
    h('div')
    h('div', { id: 'foo' })
  • attribute 和 property 都能在 prop 中书写

    https://www.doubao.com/thread/w0b21c1cca8c7e48c![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/d50a3fc95e414915808e1396850a12bf.png)

    js 复制代码
    h('div', { class: 'bar', innerHTML: 'hello' })
  • .prop.attr 这样的的属性修饰符,可以分别通过 .^ 前缀来添加

    js 复制代码
    h('div', { '.name': 'some-name', '^width': '100' })
  • 类与样式可以像在模板中一样,用数组或对象的形式书写

    js 复制代码
    h('div', { class: [foo, { bar }], style: { color: 'red' } })
  • 事件监听器应以 onXxx 的形式书写

    js 复制代码
    h('div', { onClick: () => {} })
  • children 可以是一个字符串

    js 复制代码
    h('div', { id: 'foo' }, 'hello')
  • 没有 props 时可以省略不写

    js 复制代码
    h('div', 'hello')
    h('div', [h('span', 'hello')])
  • children 数组可以同时包含 vnodes 与字符串

    js 复制代码
    h('div', ['hello', h('span', 'hello')])

类与样式的灵活写法

js 复制代码
const isActive = true
const size = 'large'

h('div', {
  // 类名支持数组/对象组合
  class: [
    'container',
    { 'active': isActive },
    { [`size-${size}`]: size }
  ],
  // 样式支持对象/字符串
  style: {
    color: 'red',
    fontSize: '16px',
    ...(isActive ? { fontWeight: 'bold' } : {})
  }
})

Fragment 支持(Vue 3 特有)

Fragment 是一种虚拟 DOM 概念,简单说就是允许组件返回多个根节点(虚拟节点,VNode ),而无需用一个额外标签包裹 。

js 复制代码
// 返回多个节点(自动包裹在Fragment中)
h('div', [
  h('h1', '标题'),
  h('p', '内容1'),
  h('p', '内容2')
])

三、props 深入:渲染函数中的数据传递机制

props 是用于描述虚拟节点对应的 DOM 元素或组件的属性、事件等信息的对象

3.1 props 的本质与作用

  • 组件通信桥梁:在渲染函数中,props是父组件向子组件传递数据的对象,等价于模板中的v-bind属性绑定。
  • 类型安全:通过声明props的类型、默认值和必填项,实现数据校验,提升组件健壮性。

3.2 在渲染函数中使用 props

  • 3.2.1 传递 props 给组件
js 复制代码
import ChildComponent from './ChildComponent.vue'

// 父组件中用h函数传递props
h(ChildComponent, {
  title: '子组件标题',
  count: 10,
  user: { name: '张三', age: 25 },
  onUpdate: (val) => console.log('更新值:', val)
})
  • 3.2.2 子组件声明 props(Vue 3 示例)
js 复制代码
<template>
  <div>
    <h3>{{ title }}</h3>
    <p>用户:{{ user.name }} ({{ user.age }})</p>
  </div>
</template>

<script setup>
import { defineProps } from 'vue'

// 声明props(对象形式)
const props = defineProps({
  title: {
    type: String,
    default: '默认标题'
  },
  count: {
    type: Number,
    required: true
  },
  user: {
    type: Object,
    default: () => ({ name: '未知', age: 0 })
  }
})
</script>

3.3 属性修饰符:精准控制 prop 与 attribute

  • .prop修饰符(明确作为组件 prop)
js 复制代码
// 子组件声明props: { name: String }
h(ChildComponent, { 
  '.name': '李四'  // 等价于模板中的:name="李四"
})
  • ^attr修饰符(明确作为 DOM attribute)
js 复制代码
// 渲染为<div data-id="123"></div>
h('div', { 
  '^data-id': '123'  // 强制作为HTML attribute
})

// 给组件的根元素添加attribute(即使组件未声明该prop)
h(ChildComponent, { 
  '^data-role': 'admin'  // 会出现在组件根元素的DOM上
})

四、实战案例:渲染函数与 props 的组合应用

4.1 动态生成表单组件

js 复制代码
import { h, ref } from 'vue'

// 表单字段配置
const fields = [
  { type: 'text', label: '姓名', model: 'name' },
  { type: 'number', label: '年龄', model: 'age' },
  { type: 'checkbox', label: '同意协议', model: 'agreed' }
]

// 表单数据
const formData = ref({
  name: '张三',
  age: 25,
  agreed: false
})

// 渲染函数
export default {
  setup() {
    return () => h('form', { class: 'dynamic-form' }, [
      // 循环生成表单项
      fields.map(field => h('div', { class: 'form-group' }, [
        h('label', null, field.label),
        h('input', {
          type: field.type,
          id: field.model,
          value: formData.value[field.model],
          onChange: (e) => {
            formData.value[field.model] = 
              field.type === 'checkbox' ? e.target.checked : e.target.value
          }
        })
      ]),
      // 提交按钮
      h('button', { 
        type: 'submit',
        class: 'btn-primary',
        onClick: (e) => {
          e.preventDefault()
          console.log('表单提交:', formData.value)
        }
      }, '提交')
    ])
  }
}

4.2 高阶组件(HOC)封装

js 复制代码
import { h } from 'vue'

// 权限验证HOC
const withAuth = (Component) => {
  return {
    setup() {
      const isAuthenticated = ref(false)
      // 模拟权限验证
      setTimeout(() => {
        isAuthenticated.value = true // 假设验证通过
      }, 1000)

      return () => isAuthenticated.value 
        ? h(Component) // 有权限时渲染组件
        : h('div', { class: 'auth-error' }, '无访问权限')
    }
  }
}

// 使用HOC
const ProtectedComponent = withAuth({
  setup() {
    return () => h('div', '受保护的内容')
  }
})

// 渲染HOC组件
h(ProtectedComponent)

五、性能优化与最佳实践

5.1 合理使用 key 提升 diff 效率

js 复制代码
// 列表渲染时指定key
h('ul', null, 
  list.map(item => h('li', { key: item.id }, item.text))
)

5.2 避免过度使用渲染函数

  • 优先模板:模板语法足以满足需求时,避免强行使用渲染函数,保持代码可读性。
  • 组件封装:将复杂渲染逻辑封装为组件,通过 props 和事件暴露接口,而非在单个组件中堆砌渲染函数。

5.3 结合 JSX 提升开发效率

js 复制代码
// 需要配置Babel/TS支持JSX
import { h } from 'vue'
const app = () => (
  <div class="app">
    <h1>JSX标题</h1>
    <button onClick={() => alert('点击')}>操作按钮</button>
    <ChildComponent :count={10} />
  </div>
)	

六、Vue 2 与 Vue 3 中渲染函数的差异

七、总结:渲染函数的适用场景与学习建议

7.1 推荐使用场景

  • 动态组件生成:根据 API 数据动态决定组件结构(如表单生成器、可视化编辑器)。

  • 高阶组件封装:实现权限控制、加载状态等通用逻辑的复用。

  • 性能敏感场景:高频更新的列表或图表组件,避免模板编译开销。

    一般不使用h函数,大多数情况下,当我们使用的第三方库不满足我们需要的api时,但是有需要对其样式进行修改,可以使用h函数动态修改

相关推荐
崔庆才丨静觅11 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606111 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了12 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅12 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅12 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅12 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment12 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅13 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊13 小时前
jwt介绍
前端
爱敲代码的小鱼13 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax