目录
透传 Attributes
Attributes 继承
对 class 和 style 的合并
v-on 监听器继承
深层组件继承
禁用 Attributes 继承
多根节点的 Attributes 继承
vue2 $attrs 和 $listeners
$attrs 概念说明
$attrs 案例
$listeners 概念说明
$listeners案例
vue3 $attrs 和 $listeners
attrs在 template 中的用法
只有1个根元素的情况下
有2个根元素的情况下
$listeners 弃用
attrs在 js 中的用法
Options API
Composition API 3.0的语法
Composition API 3.2的语法
总结
移除 $listeners
概览
2.x 语法
3.x 语法
$attrs 包含 class & style
概览
2.x 行为
3.x 行为
(1)$props:当前组件接收到的 props 对象。Vue 实例代理了对其 props 对象属性的访问。
(2)$attrs:包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。
(3)$listeners:包含了父作用域中(不含 .native 修饰器的)v-on事件监听器。他可以通过 v-on="listeners"传入内部组件
透传 Attributes 是指由父组件传入,且没有被子组件声明为 props 或是组件自定义事件的 attributes 和事件处理函数。
“透传 attribute”指的是传递给一个组件,却没有被该组件声明为 props 或 emits 的 attribute 或者 v-on
事件监听器。最常见的例子就是 class
、style
和 id
。
当一个组件以单个元素为根作渲染时,透传的 attribute 会自动被添加到根元素上。举例来说,假如我们有一个
组件,它的模板长这样:
一个父组件使用了这个组件,并且传入了 class
:
最后渲染出的 DOM 结果是:
这里,
并没有将 class
声明为一个它所接受的 prop,所以 class
被视作透传 attribute,自动透传到了
的根元素上。
class
和 style
的合并如果一个子组件的根元素已经有了 class
或 style
attribute,它会和从父组件上继承的值合并。如果我们将之前的
组件的模板改成这样:
则最后渲染出的 DOM 结果会变成:
v-on
监听器继承同样的规则也适用于 v-on
事件监听器
click
监听器会被添加到
的根元素,即那个原生的 元素之上。当原生的
被点击,会触发父组件的
onClick
方法。同样的,如果原生 button
元素自身也通过 v-on
绑定了一个事件监听器,则这个监听器和从父组件继承的监听器都会被触发。
有些情况下一个组件会在根节点上渲染另一个组件。例如,我们重构一下
,让它在根节点上渲染
:
此时
接收的透传 attribute 会直接继续传给
。
请注意:
透传的 attribute 不会包含
上声明过的 props 或是针对
emits
声明事件的v-on
侦听函数,换句话说,声明过的 props 和侦听函数被“消费”了。
透传的 attribute 若符合声明,也可以作为 props 传入
。
如果你不想要一个组件自动地继承 attribute,你可以在组件选项中设置 inheritAttrs: false
。
最常见的需要禁用 attribute 继承的场景就是 attribute 需要应用在根节点以外的其他元素上。通过设置 inheritAttrs
选项为 false
,你可以完全控制透传进来的 attribute 被如何使用。
vue2
Vue 2 的虚拟 DOM 实现对
class
和style
attribute 有一些特殊处理。因此,与其它所有 attribute 不一样,它们没有被包含在$attrs
中。上述行为在使用
inheritAttrs: false
时会产生副作用:
$attrs
中的 attribute 将不再被自动添加到根元素中,而是由开发者决定在哪添加。- 但是
class
和style
不属于$attrs
,它们仍然会被应用到组件的根元素中:
vue3
从 3.3 开始你也可以直接在 中使用 defineOptions:
这些透传进来的 attribute 可以在模板的表达式中直接用 $attrs
访问到。
Fallthrough attribute: {{ $attrs }}
这个 $attrs
对象包含了除组件所声明的 props
和 emits
之外的所有其他 attribute,例如 class
,style
,v-on
监听器等等。
有几点需要注意:
和 props 有所不同,透传 attributes 在 JavaScript 中保留了它们原始的大小写,所以像
foo-bar
这样的一个 attribute 需要通过$attrs['foo-bar']
来访问。像
@click
这样的一个v-on
事件监听器将在此对象下被暴露为一个函数$attrs.onClick
。
和单根节点组件有所不同,有着多个根节点的组件没有自动 attribute 透传行为。如果 $attrs
没有被显式绑定,将会抛出一个运行时警告。
如果
有下面这样的多根节点模板,由于 Vue 不知道要将 attribute 透传到哪里,所以会抛出一个警告。
...
...
如果 $attrs
被显式绑定,则不会有警告:
...
...
不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)
v-bind="$attrs"
传入内部组件父组件
子组件
结果
不注释掉子组件的props, $attrs的值
inheritAttrs: false 和 $attrs ;配合使用解决的问题?
父作用域中的 (不含 .native 修饰器的) v-on 事件监听器
。v-on="$listeners" 将所有的事件监听器指向这个组件的某个特定的子元素
子组件(SlotContainer.vue)
结果
@click.native="testJiami"的方法没有在 $listeners中
简单来说, attrs 主要接收没在 props 里定义,但父组件又传过来的属性。
{{ msg }} - {{ $attrs }}
可以看到,在子组件中,msg 使用了 props 接收,所以 {{ msg }} 可以直接输出 props 里接收的内容。
而没在 props 里接收的内容,全部都放到了 $attrs 里,并且存在一个对象里面。
接下来将展开讲解不同情况下 attrs 的使用方法。
在前面简单的例子里其实已经大概知道 attrs 在 template 的用法。但 Vue3 中 template 不再要求只有一个根元素了。所以 attrs 在 template 中分2种情况使用。
只有1个根元素的情况下,子组件中,没被 props 接收的属性,都会绑定在根元素上。
{{ msg }}
可以看到,没被 props 接收的属性都被绑定到根元素上了。
连 style 里传入的样式也被执行,文字变成红色了。
当子组件有2个根元素时,没被 props 接收的属性不会绑定到子组件的元素上。
{{ msg }}
{{ msg }}
此时连父组件传入是 style 样式都不生效了。
如果我们此时希望第二个元素绑定所有没被 props 接收的属性,可以使用 v-bind="$attrs" 的方法实现
{{ msg }}
{{ msg }}
$listeners
对象在 Vue 3 中已被移除。事件监听器现在是 $attrs
的一部分:
{
text: '这是一个 attribute',
onClose: () => console.log('close 事件被触发')
}
除了在 template 中可以访问到 $attrs ,在 JS 中也可以访问到。
vue 3 其实是兼容大部分 Vue 2 语法的,也就是 Options API 。而 attrs 在 Options APi 和 Composition Api 中的使用方法会稍微有一丢丢区别。而 Composition API 又分为 Vue 3.2 前的语法和 3.2 后的语法。
接下来将分开讨论这3种情况。
此时控制台会输出没被 props 接收的属性。
Vue 3.2 前的写法,需要在 setup 方法里接收2个参数,而 attrs 就在 context 参数里。
Vue 3.2 后的语法,可以在
需要引入 vue 中的 useAttrs ,在调用 useAttrs 后会返回当前未被 props 接收的属性。
重点是以下两句。
import { useAttrs } from 'vue'
const attrs = useAttrs()
$listeners
$listeners
对象在 Vue 3 中已被移除。事件监听器现在是 $attrs
的一部分:
{
text: '这是一个 attribute',
onClose: () => console.log('close 事件被触发')
}
在 Vue 2 中,你可以通过 this.$attrs
访问传递给组件的 attribute,以及通过 this.$listeners
访问传递给组件的事件监听器。结合 inheritAttrs: false
,开发者可以将这些 attribute 和监听器应用到根元素之外的其它元素:
在 Vue 3 的虚拟 DOM 中,事件监听器现在只是以 on
为前缀的 attribute,这样它就成为了 $attrs
对象的一部分,因此 $listeners
被移除了。
如果这个组件接收一个 id
attribute 和一个 v-on:close
监听器,那么 $attrs
对象现在将如下所示:
{
id: 'my-input',
onClose: () => console.log('close 事件被触发')
}
$attrs
包含 class
& style
$attrs
现在包含了所有传递给组件的 attribute,包括 class
和 style
。
Vue 2 的虚拟 DOM 实现对 class
和 style
attribute 有一些特殊处理。因此,与其它所有 attribute 不一样,它们没有被包含在 $attrs
中。
上述行为在使用 inheritAttrs: false
时会产生副作用:
$attrs
中的 attribute 将不再被自动添加到根元素中,而是由开发者决定在哪添加。class
和 style
不属于 $attrs
,它们仍然会被应用到组件的根元素中:
像这样使用时:
……将生成以下 HTML:
$attrs
包含了所有的 attribute,这使得把它们全部应用到另一个元素上变得更加容易了。现在上面的示例将生成以下 HTML:
在使用了
inheritAttrs: false
的组件中,请确保样式仍然符合预期。如果你之前依赖了class
和style
的特殊行为,那么一些视觉效果可能会遭到破坏,因为这些 attribute 现在可能被应用到了另一个元素中。