news 2026/4/16 21:34:47

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
vue3使用h函数如何封装组件和$attrs和props的区别

,插槽如何穿透

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>

01

01-1

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

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

// 子组件

<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>

02

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

我们可以通过 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>

03

04

另外一种暴露方式

常见的暴露方式

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>

05

遇见问题,这是你成长的机会,如果你能够解决,这就是收获。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 11:10:54

一文搞懂鲸鱼优化算法Whale Optimization Algorithm(WOA)!!!(核心原理+数学模型+代码实战+实际应用)

文章目录 文章脉络 1 生物灵感与核心思想 1.1 座头鲸的泡泡网捕食法 1.2 从自然行为到优化算法 2 算法流程与数学模型 2.1 算法前提与初始化 2.2 数学模型与核心公式 1. 包围猎物机制 2. 气泡网攻击(螺旋更新位置) 3. 搜索猎物(全局探索) 2.3 算法完整流程 3 Python代码实现…

作者头像 李华
网站建设 2026/4/16 12:41:29

2026年度必藏!国内TOP9免费免版权可商用音频素材网站排行榜,个人/企业首选,彻底规避侵权风险,速看宝藏清单!

在视频制作、广告宣传、内容创作等场景中&#xff0c;音频素材是不可或缺的核心元素。与图片、字体等素材相同&#xff0c;音频作品受版权法保护&#xff0c;未经授权使用&#xff08;无论商用还是非商用&#xff09;都可能引发侵权纠纷。免费下载不等于免费使用&#xff0c;付…

作者头像 李华
网站建设 2026/4/16 12:14:13

结构光三维视觉与激光条纹提取方法,以及高斯模拟

结构光三维视觉与激光条纹提取方法总结第一部分&#xff1a;结构光三维视觉技术基础核心概念解析什么是结构光三维视觉&#xff1f;结构光&#xff1a;有特定几何形状的光&#xff08;这里指激光投射的平面光束/条纹&#xff09;三维视觉&#xff1a;获取物体表面的三维几何信息…

作者头像 李华
网站建设 2026/4/16 16:20:31

轻松搞定Apache Doris JDBC连接:Java应用集成实战指南

轻松搞定Apache Doris JDBC连接&#xff1a;Java应用集成实战指南 【免费下载链接】doris Apache Doris is an easy-to-use, high performance and unified analytics database. 项目地址: https://gitcode.com/gh_mirrors/dori/doris 还在为Java应用如何高效连接Apache…

作者头像 李华
网站建设 2026/4/16 12:51:50

Redis 零基础到进阶,Spring Boot 整合 Redis,笔记93-99

Redis 零基础到进阶&#xff0c;Spring Boot 整合 Redis&#xff0c;笔记93-99 一、参考资料 【尚硅谷Redis零基础到进阶&#xff0c;最强redis7教程&#xff0c;阳哥亲自带练&#xff08;附redis面试题&#xff09;】 https://www.bilibili.com/video/BV13R4y1v7sP/?p99&…

作者头像 李华
网站建设 2026/4/16 12:59:06

OkHttp终极网络请求库:10个高效开发技巧完全指南

OkHttp终极网络请求库&#xff1a;10个高效开发技巧完全指南 【免费下载链接】okhttp square/okhttp&#xff1a;这是一个基于Java的网络请求库&#xff0c;适合进行HTTP和HTTPS通信。特点包括高性能、易于使用、支持缓存和认证等。 项目地址: https://gitcode.com/gh_mirror…

作者头像 李华