vue-函数式组件

973 阅读1分钟

函数式组件概念

函数式组件有以下特点:

  • functional true
  • 无状态,无实例,没有this上下文,没有生命周期

在 Vue 2 中,函数式组件有两个主要用例: 作为性能优化,因为它们的初始化速度比有状态组件快得多 返回多个根节点 然而,在 Vue 3 中,有状态组件的性能已经提高到可以忽略不计的程度。此外,有状态组件现在还包括返回多个根节点的能力。因此,函数式组件剩下的唯一用例就是简单组件,比如创建动态标题的组件。否则,建议你像平常一样使用有状态组件。

以官方文档中通过一个level prop 动态生成标题 (heading) 的组件举例,普通写法是:

<template>
  <h1 v-if="level === 1">
    <slot></slot>
  </h1>
  <h2 v-else-if="level === 2">
    <slot></slot>
  </h2>
  <h3 v-else-if="level === 3">
    <slot></slot>
  </h3>
  <h4 v-else-if="level === 4">
    <slot></slot>
  </h4>
  <h5 v-else-if="level === 5">
    <slot></slot>
  </h5>
  <h6 v-else-if="level === 6">
    <slot></slot>
  </h6>
</template>
<script>
export default {
  name: 'heading',
  props: {
    level: {
      type: Number,
      default: 1,
      requited: true
    }
  }
}
</script>
    <heading :level="2">Heading</heading>

用render改写

<script>
export default {
  name: 'render-heading',
  created () {
    console.log('RenderHeading created')
  },
  render: function (createElement) {
    return createElement(
      'h' + this.level, // 标签名称
      this.$slots.default // 子节点数组
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
}
</script>

我们用render替换了template的写法,代码精简很多,而且此时还是带状态的组件,用created钩子函数能监听到组件的生命周期。

2.X语法

如果要改写为函数式组件,只需增加一个functional: true,,并且修改render函数为:

render (h, {props, data, children}) {
  return h(`h${props.level}`, data, children)
},

此时已经变成一个函数式组件了,没有生命周期,也就打印不出"RenderHeading created"

3.X语法

现在在 Vue 3 中,所有的函数式组件都是用普通函数创建的,换句话说,不需要定义 { functional: true } 组件选项。

他们将接收两个参数:propscontextcontext 参数是一个对象,包含组件的 attrsslots,和 emit property。

此外,现在不是在 render 函数中隐式提供 h,而是全局导入 h。

使用前面提到的 <render-heading> 组件的示例,下面是它现在的样子。

import { h } from 'vue'

const RenderHeading = (props, context) => {
  return h(`h${props.level}`, context.attrs, context.slots)
}

RenderHeading.props = ['level']

export default RenderHeading

用函数式组件做临时变量

这个用法是参考唐金州的视频教程。

我们可以通过函数式组件做临时变量,computed也可以实现临时变量,但是computed必须依赖全局响应式数据,在某些情况下你可能想用非全局的临时变量。

创建一个tempVar.js

export default {
  functional: true,
  render: (h, ctx) => {
    return ctx.scopedSlots.default && ctx.scopedSlots.default(ctx.props || {})
  }
}

这个组件直接把props属性返回给调用方,然后在传入一个template的v-slot中,在这个template内部就可以直接使用我们定义的props变量了,这就是一个临时变量。

    <TempVar
      :var1="`hello ${name}`"
    >
    <!-- 接收 prop 的具名插槽 -->
      <template v-slot="{var1}">
        {{ var1 }}
      </template>
    </TempVar>

参考

vue官方文档