vue3使用h函数封装组件和$attrs和props的区别详解

 更新时间:2025年10月09日 09:37:33   作者:南风晚来晚相识  
这篇文章主要为大家详细介绍了vue3使用h函数封装组件和$attrs和props的区别,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

二次封装组件需要考虑的3个重要的问题

1.props 如何进行传递

2.插槽如何穿透

3.暴露实例以及实例中的方法

在vue3中的$attrs的变化

  • vue3中$listeners已被删除合并到$attrs中。
  • vue3的$attrs现在包括class和style属性。
  • vue2中不包含class和style属性。

也就是说:当子组件写上 v-bind="$attrs"

父组件就可以使用子组件的内置事件和内置属性了。

下面我们会详细说一下$attrs

props 如何进行传递属性和事件

我们可以在子组件中使用 v-bind="$attrs"

这样可以把父组件中的属性传递给子组件了

// 子组件
<template>
  <div>
    <!-- v-bind="$attrs"  可以接收到父组件中的属性设置 -->
    <el-input v-bind="$attrs"></el-input>
  </div>
</template>
// 父组件
<template>
  <div>
    <MyInput class="set-width" placeholder="请输入名称" clearable v-model="name" @blur="clearHandler"></MyInput>
  </div>
</template>

<script setup lang="ts">
import MyInput from '@/components/MyInput.vue'
import { ref } from 'vue';
let name = ref('')


const clearHandler = () => {
  console.log('失去焦点啦')
  name.value += 'copy'
}
</script>

<style lang="scss" scoped>
.set-width {
  margin: 100px;
  width: 300px;
}
</style>

如何解决写组件时没有属性提示的问题

我们发现一个问题:在父组件中的组件写相关属性时,没有属性提示。

// 子组件
<template>
  <div>
    <!-- v-bind="props"  现在我们的属性肯定是 element-plus 的内置属性了 -->
    <el-input v-bind="props"></el-input>
  </div>
</template>

<script setup lang="ts">
// 引入 input 的所有属性
import { type InputProps} from 'element-plus'
// 定义 props, Partial将必填属性变成可选属性
const props = defineProps<Partial<InputProps>>()
</script>

这样父组件在使用的时候,就可以看到属性提示了。

插槽如何封装1: 通过 template 来封装插槽

<template>
  <div>
    <el-input v-bind="props">
      <!-- 插槽 -->
      <template v-for="(_, slotName) in $slots" #[slotName]>
        <slot :name="slotName"></slot>
      </template>
    </el-input>
  </div>
</template>

<script setup lang="ts">
// 引入 input 的所有属性
import { type InputProps} from 'element-plus'
// 定义 props, Partial将必填属性变成可选属性
const props = defineProps<Partial<InputProps>>()

插槽如何封装2: 通过h函数来处理插槽

我们使用h函数来进行封装。

h函数如果第1个参数如果是组件,那么第三个参数就是插槽

<template>
  <div>
    <!-- 我们使用h函数来进行封装,h函数如果第1个参数如果是组件,那么第三个参数就是插槽 -->
    <component :is="h(ElInput, {...$attrs,...props}, $slots)"></component>
  </div>
</template>

<script setup lang="ts">
import { h } from 'vue'
// 引入 input 的所有属性
import { type InputProps, ElInput} from 'element-plus'
// 定义 props, Partial将必填属性变成可选属性
const props = defineProps<Partial<InputProps>>()
</script>
// 父组件
<template>
  <div>
    <MyInput class="set-width"   placeholder="请q输入内容">
      <!-- 在组件中使用插槽 -->
      <template #prepend>
        <el-select v-model="select" placeholder="Select" style="width: 115px">
          <el-option label="Restaurant" value="1" />
          <el-option label="Order No." value="2" />
          <el-option label="Tel" value="3" />
        </el-select>
      </template>
      <template #append>.com</template>
    </MyInput>
  </div>
</template>

<script setup lang="ts">
import MyInput from '@/components/MyInput.vue'
import { ref } from 'vue';
const select = ref('1')
</script>

<style lang="scss" scoped>
.set-width {
  margin: 100px;
  width: 300px;
}
</style>

暴露实例以及实例中的方法

我们可以通过 defineExpose 来暴露实例以及方法【常用的】

也可以通过vm.exposed来进行暴露实例以及方法

需要注意组件最初设置了v-if=false这种情况

// 子组件
<template>
  <div>
    <!-- 我们使用h函数来进行封装,h函数如果第1个参数如果是组件,那么第三个参数就是插槽 -->
    <component :is="h(ElInput, {...$attrs,...props, ref: nodeRef}, $slots)"></component>
  </div>
</template>

<script setup lang="ts">
import { h, getCurrentInstance } from 'vue'
// 引入 input 的所有属性
import { type InputProps, ElInput} from 'element-plus'
// 定义 props, Partial将必填属性变成可选属性
const props = defineProps<Partial<InputProps>>()
// 获取当前组件实例
const vm = getCurrentInstance()

// ref可以是一个字符串,也可以是一个函数。这样父组件就可以通过ref访问这个组件的实例了
function  nodeRef(inputInstance) {
  // 现在我们把子组件实例给他,当组件使用了v-if=false的时候,inputInstance为null
  // 这里我们是把实例(实例中包含方法)暴露出去
  vm.exposed= inputInstance || {}
  // 代理对象也要做同步的更改
  vm.exposeProxy = inputInstance || {}
}
</script>
// 父组件
<template>
  <div>
    <MyInput class="set-width" v-model="msg" ref="NodeInputRef"  placeholder="请输入内容" @blur="clearHandler">
      <!-- 在组件中使用插槽 -->
      <template #prepend>
        <el-select v-model="select" placeholder="Select" style="width: 115px">
          <el-option label="Restaurant" value="1" />
          <el-option label="Order No." value="2" />
          <el-option label="Tel" value="3" />
        </el-select>
      </template>
      <template #append>.com</template>
    </MyInput>

    <el-button @click="getHandler">清空值</el-button>
  </div>
</template>

<script setup lang="ts">
import MyInput from '@/components/MyInput.vue'
import { ref } from 'vue';
const select = ref('1')
const msg = ref('放假快乐')

const NodeInputRef = ref(null)
// 获取实例中的方法
const getHandler = () => {
  NodeInputRef.value?.clear()
}

const clearHandler = () => {
  console.log('失去焦点啦')
}
</script>

另外一种暴露方式

常见的暴露方式

defineProps({ 
  name:xxx,
  age:xxx,
})

等价与下面这一种

vm.exposed= {
  name:xxx,
  age:xxx,
}

vue3 中的 props

props 是组件的自定义属性,用于从父组件向子组件传递数据。

props 不会包含继承的属性(如 class 和 style),除非显式声明。

vue3 中的 $attrs

vu3中$attrs: 包含了所有[传递]给[子组件]的非 props 属性。如:继承的属性(如 class 和 style)以及未在 props 中声明的属性。

vue3中的$attrs: 包含 style和class。$attrs包含着数据和事件。

vue3 $listeners已被删除合并到$attrs中。

在vue2中的$attrs

vu2中$attrs: 包含了所有[传递]给[子组件]的非 props 属性和style和class之外的属性。

vue2中的$attrs: 不包含 style和class

下面是详细的讲解:

  • 在V ue2 中,attrs里面包含着上层组件传递的所有数据(除style和class)
  • 当一个组件声明了prop时候,attrs里面包含除去prop里面的数据剩下的数据。
  • 结合inheritAttrs:false,可以将传递下来的数据应用于其他元素,而不是根元素。

h函数封装上面的组件

有些的小伙伴说:我们是否可以使用h函数去封装上面的组件呢?

<script lang="ts">
import { defineComponent, h, getCurrentInstance } from 'vue'
import { type InputProps, ElInput } from 'element-plus'
export default  {
  // 组件名称
  name: 'MyInput',
  inheritAttrs: false,
  setup(props, { attrs, slots }) {
    console.log('attrs', attrs)
    // attrs:除去props中声明的属性。包含属性和事件
    const vm = getCurrentInstance()
    function nodeRef(inputInstance: any) {
      vm.exposed = inputInstance || {}
      vm.exposeProxy = inputInstance || {}
    }
    return () => h(ElInput, {
      ...attrs,
      ...props,
      ref: nodeRef
    }, slots)
  }
}
<template>
  <div>
    <MyInput class="set-width" placeholder="请输入名称" clearable v-model="name" @blur="clearHandler"></MyInput>
  </div>
</template>

<script setup lang="ts">
import MyInput from '@/components/MyInput.vue'
import { ref } from 'vue';
let name = ref('')
const clearHandler = () => {
  console.log('失去焦点啦')
  name.value += 'copy'
}
</script>

<style lang="scss" scoped>
.set-width {
  margin: 100px;
  width: 300px;
}
</style>

到此这篇关于vue3使用h函数封装组件和$attrs和props的区别详解的文章就介绍到这了,更多相关vue3封装组件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 基于Vue和Firebase实现一个实时聊天应用

    基于Vue和Firebase实现一个实时聊天应用

    在本文中,我们将学习如何使用Vue.js和Firebase来构建一个实时聊天应用,Vue.js是一种流行的JavaScript前端框架,而Firebase是Google提供的实时数据库和后端服务,结合这两者,我们可以快速搭建一个功能强大的实时聊天应用,需要的朋友可以参考下
    2023-08-08
  • vue的三种图片引入方式代码实例

    vue的三种图片引入方式代码实例

    这篇文章主要介绍了vue的三种图片引入方式代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • 基于Vue3和element-plus实现登录功能(最终完整版)

    基于Vue3和element-plus实现登录功能(最终完整版)

    这篇文章主要介绍了基于Vue3和element-plus实现一个完整的登录功能,本文结合示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-03-03
  • Vue + Android WebView实现大文件PDF预览完整解决方案(附详细代码)

    Vue + Android WebView实现大文件PDF预览完整解决方案(附详细代码)

    这篇文章主要介绍了Vue + Android WebView实现大文件PDF预览完整解决方案的相关资料,解决了在AndroidWebView环境下预览大文件PDF的问题,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-12-12
  • vue cli3.0 引入eslint 结合vscode使用

    vue cli3.0 引入eslint 结合vscode使用

    这篇文章主要介绍了vue cli3.0 引入eslint 结合vscode使用,本文分步骤给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-05-05
  • vue--点击当前增加class,其他删除class的方法

    vue--点击当前增加class,其他删除class的方法

    今天小编就为大家分享一篇vue--点击当前增加class,其他删除class的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-09-09
  • Vue EventBus自定义组件事件传递

    Vue EventBus自定义组件事件传递

    这篇文章主要介绍了Vue EventBus自定义组件事件传递,组件化应用构建是Vue的特点之一,本文主要介绍EventBus进行数据消息传递 ,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-06-06
  • vue实现input输入模糊查询的三种方式

    vue实现input输入模糊查询的三种方式

    本文主要介绍了vue实现input输入模糊查询的三种方式吗,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • vue中v-if 和v-permission 共同使用的坑及解决方案

    vue中v-if 和v-permission 共同使用的坑及解决方案

    这篇文章主要介绍了vue中v-if 和v-permission 共同使用的坑及解决方案的相关资料,需要的朋友可以参考下
    2023-07-07
  • 前端vue数组去重的三种方法代码实例

    前端vue数组去重的三种方法代码实例

    数组去重是我面试时候经常会问到应聘者的一个问题,所以下面这篇文章主要给大家介绍了关于前端vue数组去重的三种方法,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-06-06

最新评论