首页 前端知识 vue 之 h() 函数

vue 之 h() 函数

2024-08-10 22:08:59 前端知识 前端哥 155 424 我要收藏

目录

前言

h() 函数是什么

h() 函数参数

h() 函数基本使用

h() 函数创建 vnodes

h() 函数 API


前言

vue 在绝大多数情况下都推荐使用模板来编写 html 结构,但是对于一些复杂场景下需要完全的 JS 编程能力,这个时候我们就可以使用渲染函数 ,它比模板更接近编译器

vue 在生成真实的 DOM 之前,会将我们的节点转换成 VNode,而 VNode 组合在一起形成一颗树结构,就是虚拟 DOM(VDOM)

  我们之前编写的 template 中的 HTML 最终也是使用渲染函数生成对应的 VNode

h() 函数是什么

Vue 提供了一个 h() 函数用于创建 vnodes:

import { h } from 'vue'

const vnode = h(
  'div', // type
  { id: 'foo', class: 'bar' }, // props
  [
    /* children */
  ]
)

h() 是 hyperscript 的简称——意思是“能生成 HTML (超文本标记语言) 的 JavaScript”。这个名字来源于许多虚拟 DOM 实现默认形成的约定。一个更准确的名称应该是 createVnode(),但当你需要多次使用渲染函数时,一个简短的名字会更省力。

h()函数参数

  • type:类型参数,必填。一个html标签名,一个组件或者一个异步组件,或函数式组件
  • props:props参数,非必填。一个对象,内容包括了即将创建的节点的属性,例如 idclassstyle等,节点的事件监听也是通过 props 参数进行传递,并且以 on 开头,以 onXxx 的格式进行书写,如 onInputonClick 等。不写的话最好用 null占位
  • children:子节点,非必填。内容可以是文本、虚拟 DOM 节点和插槽等等。

官方完整类型参数如下:

// 完整参数签名
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 }

h() 函数基本使用

h()函数可以在两个地方使用:

  • render 函数中
    render() {
        return h(
          "div",
          {
            class: "app",
          },
          [
            // 这里this是可以取到setup中的返回值的 
            h("h2", null, `当前计数: ${this.counter}`),
            h("button", {onclick:() => this.counter++}, "+1"),
            h("button", {onclick:() => this.counter--}, "-1"),
          ]
        );
      },
  • setup函数中
     setup() {
        const counter = ref(0);
        return () =>
          h(
            "div",
            {
              class: "app",
            },
            [
              // 这里this是可以取到setup中的返回值的
              h("h2", null, `当前计数: ${counter.value}`),
              h("button", { onclick: () => counter.value++ }, "+1"),
              h("button", { onclick: () => counter.value-- }, "-1"),
            ]
          );
      },

h() 函数创建 vnodes

  • 创建原生元素
    // 除了类型必填以外,其他的参数都是可选的
    h('div')
    h('div', { id: 'foo' })
    
    // attribute 和 property 都能在 prop 中书写
    // Vue 会自动将它们分配到正确的位置
    h('div', { class: 'bar', innerHTML: 'hello' })
    
    // 像 `.prop` 和 `.attr` 这样的的属性修饰符
    // 可以分别通过 `.` 和 `^` 前缀来添加
    h('div', { '.name': 'some-name', '^width': '100' })
    
    // 类与样式可以像在模板中一样
    // 用数组或对象的形式书写
    h('div', { class: [foo, { bar }], style: { color: 'red' } })
    
    // 事件监听器应以 onXxx 的形式书写
    h('div', { onClick: () => {} })
    
    // children 可以是一个字符串
    h('div', { id: 'foo' }, 'hello')
    
    // 没有 props 时可以省略不写
    h('div', 'hello')
    h('div', [h('span', 'hello')])
    
    // children 数组可以同时包含 vnodes 与字符串
    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')]
    })

MyComponent 组件

  render() {
    return h("div", null, [
        h("h2", null,  "hello world"),
        [
            this.$slots.default ? this.$slots.default() : h("h2", null, "我是默认插槽"),
            this.$slots.foo()
            this.$slots.bar()
        ]
    ]);    
  }

h() 函数 API

{
  // 和`v-bind:class`一样的 API
  'class': {
    foo: true,
    bar: false
  },
  // 和`v-bind:style`一样的 API
  style: {
    color: 'red',
    fontSize: '14px'
  },
  // 正常的 HTML 特性
  attrs: {
    id: 'foo'
  },
  // 组件 props
  props: {
    myProp: 'bar'
  },
  // DOM 属性
  domProps: {
    innerHTML: 'baz'
  },
  // 事件监听器基于 `on`
  // 所以不再支持如 `v-on:keyup.enter` 修饰器
  // 需要手动匹配 keyCode。
  on: {
    click: this.clickHandler
  },
  // 仅对于组件,用于监听原生事件,而不是组件内部使用
  // `vm.$emit` 触发的事件。
  nativeOn: {
    click: this.nativeClickHandler
  },
  // 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
  // 赋值,因为 Vue 已经自动为你进行了同步。
  directives: [
    {
      name: 'my-custom-directive',
      value: '2',
      expression: '1 + 1',
      arg: 'foo',
      modifiers: {
        bar: true
      }
    }
  ],
  // Scoped slots in the form of
  // { name: props => VNode | Array<VNode> }
  scopedSlots: {
    default: props => createElement('span', props.text)
  },
  // 如果组件是其他组件的子组件,需为插槽指定名称
  slot: 'name-of-slot',
  // 其他特殊顶层属性
  key: 'myKey',
  ref: 'myRef'
}

转载请注明出处或者链接地址:https://www.qianduange.cn//article/15222.html
标签
评论
发布的文章

安装Nodejs后,npm无法使用

2024-11-30 11:11:38

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!