使用场景
一般在项目中,我们直接使用第三方库,比如element-ui,或者是一些其它的库,这些库中早就写好了他们自己有的组件。
在实际开发项目中,我们可能需要根据业务来进行对第三方库组件的封装,从而实现一个符合自己业务的封装组件,本质上而言就是进行组件的二次封装。
对于组件的二次封装,我们需要了解属性透传的概念,大概意思就是我们调用的组件传递的属性直接传递到第三方库组件中,数据流的方向是这样的:前端调用传递属性数据->已经二次封装的组件的属性数据->第三方原生组件属性数据。
$attrs
概念
继承所有的父组件属性(没有通过 props 接收的属性还有 class 类名 和 style 样式 )
简单来说:$attrs就是一个容器对象,这个容器对象会存放:父组件传过来的且子组件未使用props声明接收的数据(除了class 类名 和 style 样式) 数据流向:上层数据往下传,下层组件接受上层的数据

背景引入
组件结构

在爷组件中,给父组件传递了是三个属性

在父组件中,通过props接受message属性

能够正常显示,但是对于没有使用props定义的属性会绑定到父节点上

会发现元素节点有"aa""bb"属性(没有使用props定义的属性)
对于没有使用props定义的属性不想让其绑定在节点上,可以使用inheritAttrs
如果不想绑定在节点上,这时可以使用inheritAttrs。inheritAttrs的默认值为true,将inheritAttrs的值设为false,这些默认的行为会被禁止
因不想在父组件节点上有该属性,则在父组件中设置

此时就没有了

注意:这个选项不影响 class 和 style 绑定。
$attrs的基本使用
打印$attrs
,是个对象,那么也可以通过$attrs.aa
的方式拿到aa的属性值

在父组件中,使用v-bind:$attrs
方法传递给子组件,子组件也就可以通过定义props的方式拿到数据,或者使用$attrs的方式拿到未定义属性的数据



会发现子组件节点上有未定义props的bb属性值,这时又可以在子组件中使用'inheritAttrs: false'
所以,通过$attrs传递属性信息的时候,如果不想在其节点上显示其属性,可以配合'inheritAttrs: false'一起使用
$listeners
概念
包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件------在创建更高层次的组件时非常有用。
可以理解为 下层数据往上传,上层组件接受下层的数据
基本使用
1.在爷组件定义接受数据的事件,并在父组件上绑定

2.父组件使用v-on="$listeners",把爷组件绑定父组件上的事件,又在子组件节点上绑定

3.子组件使用this.$emit触发事件

实际效果

子组件数据成功传递给了爷组件
实际应用
在使用二次封装的第三方组件时,正常的写法应该是这样
使用二次封装第三方组件

二次封装弹窗组件

实际效果:

如果对关闭弹窗没有其他逻辑处理的话,可以对二次封装第三方组件简化成这样

注意点:
1.使用二次封装第三方组件时,控制显示弹窗的参数必须为visible


2.在二次封装弹窗组件中$attrs
和$listeners
都需要绑定

因为使用了的.sync语法糖,内部有this.$emit('update:visible', xxx)事件触发
使用建议
1.跨组件传递数据最好不要超过三层,层数过多会增加看代码的成本
2.在封装第三方组件中可以使用 <math xmlns="http://www.w3.org/1998/Math/MathML"> a t t r s 和 attrs和 </math>attrs和listeners对代码进行简化,对于其他场景尽量使用props的方式进行数据传递
vue3中对于$attrs
和$listeners
的处理可参考这篇文章:vue3中的$attrs的变化与inheritAttrs的使用 - 掘金 (juejin.cn)