Vue h函数到底是个啥?

news2025/2/20 14:27:02

h 到底是个啥?



对于了解或学习Vue高阶组件(HOC)的同学来说,h() 函数无疑是一个经常遇到的概念。
在这里插入图片描述
那么,这个h() 函数究竟如何使用呢,又在什么场景下适合使用呢?



一、h 是什么

看到这个函数你可能会有些许困惑,为什么叫h呢?代表着什么呢?

官方定义

返回一个“虚拟节点” ,通常缩写为 VNode: 一个普通对象,其中包含向 Vue 描述它应该在页面上呈现哪种节点的信息,包括对任何子节点的描述。用于手动编写render

h其实代表的是 hyperscript 。它是 HTML 的一部分,表示的是超文本标记语言,当我们正在处理一个脚本的时候,在虚拟 DOM 节点中去使用它进行替换已成为一种惯例。这个定义同时也被运用到其他的框架文档中

Hyperscript 它本身表示的是 “生成描述 HTML 结构的脚本”

二、语法

h() 函数的基本语法如下:

h(tag, props, children)
  • tag:可以是字符串或组件,表示要创建的 HTML 标签或 Vue 组件。
  • props:是一个对象,包含要传递给标签或组件的属性,如类名、样式、事件监听器等。
  • children:可以是字符串、数组或函数,表示子节点。子节点可以是文本、其他 VNode 或一个返回 VNode 的函数。

以下是一个简单的代码示例,展示了如何使用 h() 函数创建一个包含标题和段落的组件:


  <!DOCTYPE html>
  <html>
    <head>
    <meta charset="utf-8">
    <title>h()</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.0-rc.4/vue.global.js"></script>
    </head>
    <body>
       <div id="app">
       </div>
       <script>
        const App = {
          render() {
            return Vue.h('h1', {}, '二川兄弟')
          }
        }
        // console 结果请在控制台查看
        console.log(Vue.h('h1', {}, '二川兄弟'))
        Vue.createApp(App).mount('#app')
      </script>

    </body>
  </html>

效果:
在这里插入图片描述

打印:
在这里插入图片描述

在这个示例中,我们创建了一个 <h1> 容器,内部插入文字 “二川兄弟”。所有这些都是通过 h() 函数来实现的,而不是使用模板语法。

三、源码解析

要深入理解 h() 函数,我们需要查看 Vue.js 的源码。在 Vue.js 的实现中,h() 函数是一个封装了 VNode 创建逻辑的工具函数。它接收标签名、属性和子节点作为参数,并返回一个包含这些信息的 VNode 对象。

export function h(type: any, propsOrChildren?: any, children?: any): VNode {
  if (arguments.length === 2) {
    if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {
      // single vnode without props
      if (isVNode(propsOrChildren)) {
        return createVNode(type, null, [propsOrChildren])
      }
      // props without children
      return createVNode(type, propsOrChildren)
    } else {
      // omit props
      return createVNode(type, null, propsOrChildren)
    }
  } else {
    if (isVNode(children)) {
      children = [children]
    }
    return createVNode(type, propsOrChildren, children)
  }
}

VNode 是 Vue.js 对真实 DOM 的一种轻量级表示。它包含了节点的类型、属性、子节点等信息,但不包含具体的 DOM 元素。Vue.js 会在渲染时将 VNode 转换为真实的 DOM 元素。

h() 函数的源码中,你会看到大量的边界情况处理和类型检查。例如,如果子节点是一个字符串,它会被转换为一个文本节点;如果子节点是一个数组,它会遍历数组并递归地创建子节点的 VNode;如果子节点是一个函数,它会调用该函数并传递当前上下文来创建子节点的 VNode。

_createVNode 又做了啥?

function _createVNode(
  type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
  props: (Data & VNodeProps) | null = null,
  children: unknown = null,
  // 更新标志
  patchFlag: number = 0,
  // 自定义属性
  dynamicProps: string[] | null = null,
  // 是否是动态节点,(v-if v-for)
  isBlockNode = false 
): VNode {
  // type必传参数
  if (!type || type === NULL_DYNAMIC_COMPONENT) {
    if (__DEV__ && !type) {
      warn(`Invalid vnode type when creating vnode: ${type}.`)
    }
    type = Comment
  }

  // Class 类型的type标准化
  // class component normalization.
  if (isFunction(type) && '__vccOpts' in type) {
    type = type.__vccOpts
  }

  // class & style normalization.
  if (props) {
    // props 如果是响应式,clone 一个副本
    if (isProxy(props) || InternalObjectKey in props) {
      props = extend({}, props)
    }
    let { class: klass, style } = props

    // 标准化class, 支持 string , array, object 三种形式
    if (klass && !isString(klass)) {
      props.class = normalizeClass(klass)
    }

    // 标准化style, 支持 array ,object 两种形式 
    if (isObject(style)) {
      // reactive state objects need to be cloned since they are likely to be
      // mutated
      if (isProxy(style) && !isArray(style)) {
        style = extend({}, style)
      }
      props.style = normalizeStyle(style)
    }
  }

  // encode the vnode type information into a bitmap
  const shapeFlag = isString(type)
    ? ShapeFlags.ELEMENT
    : __FEATURE_SUSPENSE__ && isSuspense(type)
      ? ShapeFlags.SUSPENSE
      : isTeleport(type)
        ? ShapeFlags.TELEPORT
        : isObject(type)
          ? ShapeFlags.STATEFUL_COMPONENT
          : isFunction(type)
            ? ShapeFlags.FUNCTIONAL_COMPONENT
            : 0

  if (__DEV__ && shapeFlag & ShapeFlags.STATEFUL_COMPONENT && isProxy(type)) {
    type = toRaw(type)
    warn(
      `Vue received a Component which was made a reactive object. This can ` +
        `lead to unnecessary performance overhead, and should be avoided by ` +
        `marking the component with \`markRaw\` or using \`shallowRef\` ` +
        `instead of \`ref\`.`,
      `\nComponent that was made reactive: `,
      type
    )
  }

  // 构造 VNode 模型
  const vnode: VNode = {
    __v_isVNode: true,
    __v_skip: true,
    type,
    props,
    key: props && normalizeKey(props),
    ref: props && normalizeRef(props),
    scopeId: currentScopeId,
    children: null,
    component: null,
    suspense: null,
    dirs: null,
    transition: null,
    el: null,
    anchor: null,
    target: null,
    targetAnchor: null,
    staticCount: 0,
    shapeFlag,
    patchFlag,
    dynamicProps,
    dynamicChildren: null,
    appContext: null
  }

  normalizeChildren(vnode, children)

  // presence of a patch flag indicates this node needs patching on updates.
  // component nodes also should always be patched, because even if the
  // component doesn't need to update, it needs to persist the instance on to
  // the next vnode so that it can be properly unmounted later.

  // patchFlag 标志存在表示节点需要更新,组件节点一直存在 patchFlag,因为即使不需要更新,它需要将实例持久化到下一个 vnode,以便以后可以正确卸载它
  if (
    shouldTrack > 0 &&
    !isBlockNode &&
    currentBlock &&
    // the EVENTS flag is only for hydration and if it is the only flag, the
    // vnode should not be considered dynamic due to handler caching.
    patchFlag !== PatchFlags.HYDRATE_EVENTS &&
    (patchFlag > 0 ||
      shapeFlag & ShapeFlags.SUSPENSE ||
      shapeFlag & ShapeFlags.TELEPORT ||
      shapeFlag & ShapeFlags.STATEFUL_COMPONENT ||
      shapeFlag & ShapeFlags.FUNCTIONAL_COMPONENT)
  ) {
    // 压入 VNode 栈
    currentBlock.push(vnode)
  }

  return vnode
}

四、使用场景

h() 函数在 Vue.js 中有多种使用场景,以下是一些常见的场景:

  1. 渲染函数

    • 当需要完全控制组件的渲染逻辑时,可以使用渲染函数,并在其中使用 h() 函数来创建 VNode。这种方式提供了比模板语法更高的灵活性和控制力。

    以下是一个使用渲染函数的示例,展示了如何根据条件动态渲染不同的内容:

    import { h } from 'vue';
    
    export default {
      props: ['isLoggedIn'],
      render() {
        return h('div', 
          {}, 
          this.isLoggedIn 
            ? h('p', {}, 'Welcome back!') 
            : h('p', {}, 'Please log in.')
        );
      }
    };
    
  2. 高阶组件(HOC)

    • 高阶组件是一种模式,它接收一个组件作为参数,并返回一个新的组件。在创建新组件的过程中,h() 函数用于定制或扩展原始组件的渲染逻辑。

    以下是一个高阶组件的示例,展示了如何为组件添加额外的类名:

    function withClassName(WrappedComponent, className) {
      return {
        props: WrappedComponent.props,
        render() {
          return h(WrappedComponent, { ...this.$props, class: className });
        }
      };
    }
    
    // 使用高阶组件
    const MyComponentWithClass = withClassName(MyComponent, 'my-custom-class');
    
  3. 动态组件

    • 在需要根据条件动态渲染不同组件时,h() 函数可以方便地根据条件创建不同的 VNode。

    以下是一个动态组件的示例,展示了如何根据条件渲染不同的组件:

    import { h, defineComponent } from 'vue';
    import ComponentA from './ComponentA.vue';
    import ComponentB from './ComponentB.vue';
    
    export default defineComponent({
      props: ['condition'],
      render() {
        return this.condition 
          ? h(ComponentA, { ...this.$props }) 
          : h(ComponentB, { ...this.$props });
      }
    });
    
  4. 手动创建复杂结构

    • 在某些情况下,可能需要手动创建复杂的组件结构,而不是使用模板语法。这时,h() 函数就显得非常有用。

    以下是一个手动创建复杂结构的示例,展示了如何创建一个带有嵌套子组件的表格:

    import { h } from 'vue';
    import TableRow from './TableRow.vue';
    
    export default {
      props: ['data'],
      render() {
        return h('table', 
          {}, 
          this.data.map(row => 
            h(TableRow, { key: row.id, row: row })
          )
        );
      }
    };
    

五、其他比较

与模板语法相比,使用 h() 函数提供了更高的灵活性和控制力。模板语法更适合于简单的组件和静态内容,而 h() 函数则更适合于复杂的组件和动态内容。

此外,与 JSX 相比,h() 函数更加简洁和直接。JSX 是一种语法糖,它允许在 JavaScript 中编写类似 HTML 的代码。然而,JSX 需要额外的编译步骤,并且可能会增加代码的复杂性。而 h() 函数则是 Vue.js 内置的工具函数,无需额外的编译步骤,并且更加符合 Vue.js 的设计哲学。

六、最佳实践

在使用 h() 函数时,以下是一些最佳实践:

  1. 保持简洁

    • 尽量避免在渲染函数中编写过多的逻辑。可以将复杂的逻辑拆分成多个函数或组件,以提高代码的可读性和可维护性。
  2. 使用辅助函数

    • 可以编写一些辅助函数来简化 h() 函数的使用。例如,可以编写一个函数来创建带有特定样式的节点,或者一个函数来创建带有事件监听器的节点。
  3. 避免过度使用

    • 在大多数情况下,模板语法已经足够满足需求。只有在需要更高的灵活性和控制力时,才应该考虑使用 h() 函数。
  4. 与模板语法结合使用

    • 可以将 h() 函数与模板语法结合使用。例如,可以在模板中使用 <script setup> 语法来定义渲染函数,并在其中使用 h() 函数来创建复杂的节点结构。

七、总结

在这里插入图片描述

h() 函数是 Vue.js 框架中一个强大且灵活的工具,用于在渲染函数中创建虚拟 DOM 节点。通过深入理解 h() 函数的语法、源码和使用场景,开发者可以更好地掌握 Vue.js 的渲染机制,并在实际开发中灵活运用这一工具来创建高效、可维护的组件。无论是编写渲染函数、实现高阶组件,还是处理动态组件和复杂结构,h() 函数都是不可或缺的一部分。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2298792.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深入浅出 Python Logging:从基础到进阶日志管理

在 Python 开发过程中&#xff0c;日志&#xff08;Logging&#xff09;是不可或缺的调试和监控工具。合理的日志管理不仅能帮助开发者快速定位问题&#xff0c;还能提供丰富的数据支持&#xff0c;让应用更具可观测性。本文将带你全面了解 Python logging 模块&#xff0c;涵盖…

Android WindowContainer窗口结构

Android窗口是根据显示屏幕来管理&#xff0c;每个显示屏幕的窗口层级分为37层&#xff0c;0-36层。每层可以放置多个窗口&#xff0c;上层窗口覆盖下面的。 要理解窗口的结构&#xff0c;需要学习下WindowContainer、RootWindowContainer、DisplayContent、TaskDisplayArea、T…

2025年最新版1688平台图片搜索接口技术指南及Python实现

随着电商行业的蓬勃发展&#xff0c;1688作为国内领先的B2B交易平台&#xff0c;其商品搜索功能对于买家和卖家而言都至关重要。图片搜索作为其中的一种高级搜索方式&#xff0c;能够极大地提升用户的搜索体验和准确性。本文将详细介绍如何通过API接口实现1688平台的图片搜索功…

基于A*算法与贝塞尔曲线的路径规划与可视化:从栅格地图到平滑路径生成

引言 在机器人导航、自动驾驶和游戏开发等领域,路径规划是一个核心问题。如何高效地找到从起点到终点的最优路径,并且确保路径的平滑性和安全性,是许多应用场景中的关键挑战。本文将介绍一种结合A算法和贝塞尔曲线的路径规划方法,并通过Pygame实现可视化。我们将从栅格地图…

使用verilog 实现 cordic 算法 ----- 旋转模式

1-设计流程 ● 了解cordic 算法原理&#xff0c;公式&#xff0c;模式&#xff0c;伸缩因子&#xff0c;旋转方向等&#xff0c;推荐以下链接视频了解 cordic 算法。哔哩哔哩-cordic算法原理讲解 ● 用matlab 或者 c 实现一遍算法 ● 在FPGA中用 verilog 实现&#xff0c;注意…

【css】width:100%;padding:20px;造成超出100%宽度的解决办法 - box-sizing的使用方法 - CSS布局

问题 修改效果 解决方法 .xx {width: 100%;padding: 0 20px;box-sizing: border-box; } 默认box-sizing: content-box下&#xff0c; width 内容的宽度 height 内容的高度 宽度和高度的计算值都不包含内容的边框&#xff08;border&#xff09;和内边距&#xff08;padding&…

贪心算法_翻硬币

蓝桥账户中心 依次遍历 不符合条件就反转 题目要干嘛 你就干嘛 #include <bits/stdc.h>#define endl \n using namespace std;int main() {ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); string s; cin >> s;string t; cin >> t;int ret 0;for ( i…

深入HBase——引入

引入 前面我们通过深入HDFS到深入MapReduce &#xff0c;从设计和落地&#xff0c;去深入了解了大数据最底层的基石——存储与计算是如何实现的。 这个专栏则开始来看大数据的三驾马车中最后一个。 通过前面我们对于GFS和MapReduce论文实现的了解&#xff0c;我们知道GFS在数…

2025年02月12日Github流行趋势

项目名称&#xff1a;data-formulator 项目地址url&#xff1a;https://github.com/microsoft/data-formulator 项目语言&#xff1a;TypeScript 历史star数&#xff1a;4427 今日star数&#xff1a;729 项目维护者&#xff1a;danmarshall, Chenglong-MS, apps/dependabot, mi…

【落羽的落羽 数据结构篇】双向链表

文章目录 一、链表的分类二、双向链表1. 结构2. 申请一个新节点3. 尾部插入数据4. 头部插入数据5. 尾部删除数据6. 头部删除数据7. 在指定位置之后插入数据8. 删除指定位置节点9. 销毁链表 一、链表的分类 链表的分类实际上要从这三个方向分析&#xff1a;是否带头、单向还是双…

Golang的并发编程问题解决思路

Golang的并发编程问题解决思路 一、并发编程基础 并发与并行 在计算机领域&#xff0c;“并发”和“并行”经常被混为一谈&#xff0c;但它们有着不同的含义。并发是指一段时间内执行多个任务&#xff0c;而并行是指同时执行多个任务。在 Golang 中&#xff0c;通过 goroutines…

剑指offer第2版:搜索算法(二分/DFS/BFS)

查找本质就是排除的过程&#xff0c;不外乎顺序查找、二分查找、哈希查找、二叉排序树查找、DFS/BFS查找 一、p39-JZ3 找出数组中重复的数字&#xff08;利用特性&#xff09; 数组中重复的数字_牛客题霸_牛客网 方法1&#xff1a;全部排序再进行逐个扫描找重复。 时间复杂…

在 CentOS 上更改 SSH 默认端口以提升服务器安全性

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall ︱vue3-element-admin︱youlai-boot︱vue-uniapp-template &#x1f33a; 仓库主页&#xff1a; GitCode︱ Gitee ︱ Github &#x1f496; 欢迎点赞 &#x1f44d; 收藏 ⭐评论 …

2025年:边缘计算崛起下运维应对新架构挑战

一、引言 随着科技的飞速发展&#xff0c;2025年边缘计算正以前所未有的速度崛起&#xff0c;给运维行业带来了全新的架构挑战。在这个充满机遇与挑战的时代&#xff0c;美信时代公司的美信监控易运维管理软件成为运维领域应对这些挑战的有力武器。 二、边缘计算崛起带来的运维…

怎么理解 Spring Boot 的约定优于配置 ?

在传统的 Spring 开发中&#xff0c;大家可能都有过这样的经历&#xff1a;项目还没开始写几行核心业务代码&#xff0c;就已经在各种配置文件中耗费了大量时间。比如&#xff0c;要配置数据库连接&#xff0c;不仅要在 XML 文件里编写冗长的数据源配置&#xff0c;还要处理事务…

学习总结2.14

深搜将题目分配&#xff0c;如果是两个题目&#xff0c;就可以出现左左&#xff0c;左右&#xff0c;右左&#xff0c;右右四种时间分配&#xff0c;再在其中找最小值&#xff0c;即是两脑共同处理的最小值 #include <stdio.h> int s[4]; int sum0; int brain[25][25]; …

Electron 客户端心跳定时任务调度库调研文档 - Node.js 任务调度库技术调研文档

Electron 客户端心跳定时任务调度库调研文档 - Node.js 任务调度库技术调研文档 本文将对七个流行的定时任务调度库&#xff1a;node-cron、rxjs、bull、node-schedule、agenda、bree、cron。这些库都可以用来处理定时任务&#xff0c;但它们的特点和适用场景有所不同。我们将从…

【学术投稿-第四届智能电网和绿色能源国际学术会议(ICSGGE 2025)】CSS基本选择器详解:掌握基础,轻松布局网页

可线上 官网&#xff1a;www.icsgge.org 时间&#xff1a;2025年2月28-3月2日 目录 前言 一、基本选择器简介 1. 元素选择器&#xff08;Type Selector&#xff09; 基本语法 示例 注意事项 2. 类选择器&#xff08;Class Selector&#xff09; 基本语法 示例 注意…

singleTaskAndroid的Activity启动模式知识点总结

一. 前提知识 1.1. 任务栈知识 二. Activity启动模式的学习 2.1 standard 2.2 singleTop 2.3.singleTask 2.4.singleInstance 引言&#xff1a; Activity作为四大组件之一&#xff0c;也可以说Activity是其中最重要的一个组件&#xff0c;其负责调节APP的视图&#xff…

Vue 入门到实战 十

第10章 Vue Router​​​​​​​ 目录 10.1 什么是路由 10.2 Vue Router的安装 10.2.1 本地独立版本方法 10.2.2 CDN方法 10.2.3 NPM方法 10.2.4 命令行工具&#xff08;Vue CLI&#xff09;方法 10.3 Vue Router的基本用法 10.3.1 跳转与传参 10.3.2 配置路由 10.…