目录
(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 会自动被添加到根元素上。举例来说,假如我们有一个 组件,它的模板长这样:
- <button>click mebutton>
一个父组件使用了这个组件,并且传入了 class:
<MyButton class="large" />
最后渲染出的 DOM 结果是:
<button class="large">click me</button>
这里, 并没有将 class 声明为一个它所接受的 prop,所以 class 被视作透传 attribute,自动透传到了 的根元素上。
class 和 style 的合并如果一个子组件的根元素已经有了 class 或 style attribute,它会和从父组件上继承的值合并。如果我们将之前的 组件的模板改成这样:
- <button class="btn">click mebutton>
则最后渲染出的 DOM 结果会变成:
<button class="btn large">click me</button>
v-on 监听器继承同样的规则也适用于 v-on 事件监听器
<MyButton @click="onClick" />
click 监听器会被添加到 的根元素,即那个原生的 元素之上。当原生的 被点击,会触发父组件的 onClick 方法。同样的,如果原生 button 元素自身也通过 v-on 绑定了一个事件监听器,则这个监听器和从父组件继承的监听器都会被触发。
有些情况下一个组件会在根节点上渲染另一个组件。例如,我们重构一下 ,让它在根节点上渲染 :
- <BaseButton />
此时 接收的透传 attribute 会直接继续传给 。
请注意:
透传的 attribute 不会包含
上声明过的 props 或是针对emits声明事件的v-on侦听函数,换句话说,声明过的 props 和侦听函数被“消费”了。透传的 attribute 若符合声明,也可以作为 props 传入
。
如果你不想要一个组件自动地继承 attribute,你可以在组件选项中设置 inheritAttrs: false。
最常见的需要禁用 attribute 继承的场景就是 attribute 需要应用在根节点以外的其他元素上。通过设置 inheritAttrs 选项为 false,你可以完全控制透传进来的 attribute 被如何使用。
vue2
Vue 2 的虚拟 DOM 实现对
class和styleattribute 有一些特殊处理。因此,与其它所有 attribute 不一样,它们没有被包含在$attrs中。上述行为在使用
inheritAttrs: false时会产生副作用:
$attrs中的 attribute 将不再被自动添加到根元素中,而是由开发者决定在哪添加。- 但是
class和style不属于$attrs,它们仍然会被应用到组件的根元素中:
- <template>
- <label>
- <input type="text" v-bind="$attrs" />
- </label>
- </template>
- <script>
- export default {
- inheritAttrs: false
- }
- </script>
vue3
从 3.3 开始你也可以直接在 中使用 defineOptions:
- <script setup>
- defineOptions({
- inheritAttrs: false
- })
- // ...setup 逻辑
- </script>
这些透传进来的 attribute 可以在模板的表达式中直接用 $attrs 访问到。
<span>Fallthrough attribute: {{ $attrs }}span>
这个 $attrs 对象包含了除组件所声明的 props 和 emits 之外的所有其他 attribute,例如 class,style,v-on 监听器等等。
有几点需要注意:
和 props 有所不同,透传 attributes 在 JavaScript 中保留了它们原始的大小写,所以像
foo-bar这样的一个 attribute 需要通过$attrs['foo-bar']来访问。像
@click这样的一个v-on事件监听器将在此对象下被暴露为一个函数$attrs.onClick。
和单根节点组件有所不同,有着多个根节点的组件没有自动 attribute 透传行为。如果 $attrs 没有被显式绑定,将会抛出一个运行时警告。
<CustomLayout id="custom-layout" @click="changeValue" />
如果 有下面这样的多根节点模板,由于 Vue 不知道要将 attribute 透传到哪里,所以会抛出一个警告。
- <header>...</header>
- <main>...</main>
- <footer>...</footer>
如果 $attrs 被显式绑定,则不会有警告:
- <header>...</header>
- <main v-bind="$attrs">...</main>
- <footer>...</footer>
不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)v-bind="$attrs"传入内部组件父组件
- <template>
- <SlotContainer
- ref="slotContainer"
- name="huangbiao"
- :isOk="false"
- :option="{ a: 1, b: true, c: 'ddd' }"
- >
- </SlotContainer>
- </template>
-
- <script>
- import SlotContainer from "./SlotContainer"
-
- export default {
- data() {
- return {};
- },
- components: {
- SlotContainer,
- }
- };
- </script>
-
- <style lang="scss" scoped></style>
子组件
- <script>
- export default {
- data() {
- return {};
- },
- props: {
- option: {
- type: Object,
- default: function() {
- return {};
- }
- }
- },
- mounted() {
- console.log(this.$attrs);
- },
- methods: {}
- };
- script>
结果
不注释掉子组件的props, $attrs的值

注释掉子组件的props, $attrs的值
inheritAttrs: false 和 $attrs ;配合使用解决的问题?
父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。v-on="$listeners" 将所有的事件监听器指向这个组件的某个特定的子元素- <template>
- <div class>
- <SlotContainer
- ref="slotContainer"
- v-on:m1="m1"
- v-on:m2="m2"
- @m3="m3"
- @m4="m4"
- @click.native="testJiami"
- >
- </SlotContainer>
- </div>
- </template>
-
- <script>
- import SlotContainer from "./SlotContainer";
- import CryptoJS from "crypto-js";
- export default {
- data() {
- return {};
- },
- components: {
- SlotContainer,
- },
- methods: {
- testJiami() {
- this.m1();
- this.m2();
- this.m3();
- this.m4();
- },
- m1() {
- console.log("加密结果一 MD5:" + CryptoJS.MD5("你好"));
- // 加盐 对应的API
- console.log("加密结果一 MD5:" + CryptoJS.HmacMD5("你好", "salt"));
- console.log(CryptoJS.SHA256("123456").toString());
- // 加盐 对应的API
- console.log(CryptoJS.HmacSHA256("123456", "salt").toString());
- },
- m2() {
- var pwd = "passwor";
- console.log("加密结果二 Hmac-MD5: " + CryptoJS.HmacMD5("你好", pwd));
- },
- m3() {
- var salt = CryptoJS.enc.Utf8.parse("salt"); //盐
- var iter = 1000; //迭代次数
- var mi = CryptoJS.PBKDF2("你好", salt, {
- keySize: parseInt(4),
- iterations: parseInt(iter),
- });
-
- console.log("加密结果三:" + mi);
- },
- m4() {
- var pswd = "我的密码";
- var mi = CryptoJS.AES.encrypt("你好", pswd);
- console.log("加密结果四" + mi);
- //解密
- var result = CryptoJS.AES.decrypt(mi, pswd).toString(CryptoJS.enc.Utf8);
- console.log("解密结果:" + result);
- },
- },
- };
- </script>
-
- <style lang="scss" scoped></style>
-
子组件(SlotContainer.vue)
- <script>
- export default {
- data() {
- return {};
- },
- mounted() {
- console.log(this.$listeners);
- },
- methods: {}
- };
- script>
-
- <style lang="scss" scoped>style>
-
结果

@click.native="testJiami"的方法没有在 $listeners中
简单来说, attrs 主要接收没在 props 里定义,但父组件又传过来的属性。
- <template>
- <ChildCom
- msg="雷猴"
- data="123"
- />
- template>
-
- <script setup>
- import ChildCom from './ChildCom.vue'
- script>
-
-
-
- <template>
- <div>
- {{ msg }} - {{ $attrs }}
- div>
- template>
-
- <script setup>
- defineProps({
- msg: {
- type: String
- }
- })
- script>

可以看到,在子组件中,msg 使用了 props 接收,所以 {{ msg }} 可以直接输出 props 里接收的内容。
而没在 props 里接收的内容,全部都放到了 $attrs 里,并且存在一个对象里面。
接下来将展开讲解不同情况下 attrs 的使用方法。
在前面简单的例子里其实已经大概知道 attrs 在 template 的用法。但 Vue3 中 template 不再要求只有一个根元素了。所以 attrs 在 template 中分2种情况使用。
只有1个根元素的情况下,子组件中,没被 props 接收的属性,都会绑定在根元素上。
- <!-- 父组件 ParentCom.vue -->
- <template>
- <ChildCom
- msg="雷猴"
- data="123"
- name="鲨鱼辣椒"
- style="color: red;"
- />
- </template>
-
- <script setup>
- import ChildCom from './ChildCom.vue'
- </script>
-
-
-
- <!-- 子组件 ChildCom.vue -->
- <template>
- <div>
- {{ msg }}
- </div>
- </template>
-
- <script setup>
- defineProps({
- msg: {
- type: String
- }
- })
- </script>

可以看到,没被 props 接收的属性都被绑定到根元素上了。
连 style 里传入的样式也被执行,文字变成红色了。
当子组件有2个根元素时,没被 props 接收的属性不会绑定到子组件的元素上。
- <!-- 父组件 ParentCom.vue -->
- <template>
- <ChildCom
- msg="雷猴"
- data="123"
- name="鲨鱼辣椒"
- style="color: red;"
- />
- </template>
-
- <script setup>
- import ChildCom from './ChildCom.vue'
- </script>
-
-
-
- <!-- 子组件 ChildCom.vue -->
- <template>
- <div>
- {{ msg }}
- </div>
- <div>
- {{ msg }}
- </div>
- </template>
-
- <script setup>
- defineProps({
- msg: {
- type: String
- }
- })
- </script>

此时连父组件传入是 style 样式都不生效了。
如果我们此时希望第二个元素绑定所有没被 props 接收的属性,可以使用 v-bind="$attrs" 的方法实现
- <!-- 父组件 ParentCom.vue -->
- <template>
- <ChildCom
- msg="雷猴"
- data="123"
- name="鲨鱼辣椒"
- style="color: red;"
- />
- </template>
-
- <script setup>
- import ChildCom from './ChildCom.vue'
- </script>
-
-
-
- <!-- 子组件 ChildCom.vue -->
- <template>
- <div>
- {{ msg }}
- </div>
- <div v-bind="$attrs">
- {{ msg }}
- </div>
- </template>
-
- <script setup>
- defineProps({
- msg: {
- type: String
- }
- })
- </script>

$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种情况。
- <template>
- <ChildCom
- msg="雷猴"
- data="123"
- name="鲨鱼辣椒"
- style="color: red;"
- />
- template>
-
- <script setup>
- import ChildCom from './ChildCom.vue'
- script>
-
-
-
- <script>
- export default {
- props: {
- msg: {
- type: String
- }
- },
- mounted() {
- console.log(this.$attrs)
- }
- }
- script>

此时控制台会输出没被 props 接收的属性。
- <template>
- <ChildCom
- msg="雷猴"
- data="123"
- name="鲨鱼辣椒"
- style="color: red;"
- />
- template>
-
- <script setup>
- import ChildCom from './ChildCom.vue'
- script>
-
-
-
- <script>
- export default {
- props: {
- msg: {
- type: String
- }
- },
- setup(props, context) {
- console.log('props: ', props)
- console.log('attrs: ', context.attrs)
- }
- }
- script>
Vue 3.2 前的写法,需要在 setup 方法里接收2个参数,而 attrs 就在 context 参数里。
Vue 3.2 后的语法,可以在