$attrs
與$listeners
的主要應用是實現多層嵌套傳遞。
組件A與組件B通信一般都會使用組件B中轉,即A傳遞給B,B再給C,但是如果A到C組件之間嵌套的組件過多,需要傳遞的事件和屬性較多,會導致代碼繁瑣,代碼維護困難。在vue2.4中,爲了解決該需求,引入了$attrs
和$listeners
,新增了inheritAttrs
選項。
$attrs的使用
官方定義:
包含了父作用域中不作爲prop
被識別 (且獲取) 的特性綁定 (class
和 style
除外)。當一個組件沒有聲明任何 prop
時,這裏會包含所有父作用域的綁定 (class
和 style
除外),並且可以通過 v-bind="$attrs"
傳入內部組件——在創建高級別的組件時非常有用。
(好抽象一臉懵逼。。。)
$attrs
只代表的是那些沒有被聲明爲props
的屬性,如果某個prop
被子組件中聲明瞭(就是這個屬性已經在子組件的props中了),在子組件中的$attr
會把聲明的prop
剔除。
個人理解: 一個組件在父組件中被引用,$attrs
就是組件標籤上的靜態屬性值(attr
)和動態屬性值(:attr
)的對象集合,這個集合不包含class
, style
和事件屬性
// 父組件
<child-com class="com" name="attr" :foo="foo" :boo="boo" :coo="coo" :doo="doo" @click="test"></child-com>
...
data() {
return {
foo: 'Hello World!',
boo: 'Hello Javascript!',
coo: 'Hello Vue',
doo: 'Last'
}
},
...
// child-com.vue
export default {
name: 'childCom',
components: {
childCom2
},
created() {
console.log(this.$attrs) // {name: "attr", foo: "Hello World!", boo: "Hello Javascript!", coo: "Hello Vue", doo: "Last"}
}
}
如果子組件聲明瞭$prop
,$attrs
中與$props
相同的屬性會被移除
// child-com.vue
export default {
name: 'childCom',
props: ['foo'], // foo被聲明
components: {
childCom2
},
created() {
console.log(this.$attrs) // {name: "attr", boo: "Hello Javascript!", coo: "Hello Vue", doo: "Last"}
}
}
如果childCom
裏的子組件還用到foo
,可以繼續將foo
傳給子組件
<template>
<div>
<p>foo: {{foo}} </p>
<child-com2 :foo="foo" v-bind="$attrs"></child-com2>
</div>
</template>
<script>
const childCom2 = () => import('./childCom2.vue')
export default {
name: 'childCom1',
props: ['foo'], // foo作爲props屬性綁定
inheritAttrs: false,
components: {
childCom2
},
created() {
console.log(this.$attrs) // {name: "attr", boo: "Hello Javascript!", coo: "Hello Vue", doo: "Last"}
}
}
</script>
//childCom2.vue
created() {
console.log(this.$attrs) // {foo: "Hello World!", name: "attr", boo: "Hello Javascript!", coo: "Hello Vue", doo: "Last"}
}
inheritAttrs要設置成false還是true(默認)看下圖就知道
與$props
比較
$props
必須在組件中註冊了props
才能用拿到值,所以在嵌套層級比較深的組件中$attr
s拿值更加便捷
$listeners
的使用
包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監聽器。它可以通過v-on=”$listeners”
傳入內部組件。
歸納起來也是兩點:
$listeners
是組件的內置屬性,它的值是父組件(不含.native
修飾器的)v-on
事件監聽器。- 組件可以通 過在自己的子組件上使用
v-on=”$listeners”
,進一步把值傳給自己的子組件。如果子組件已經綁定$listene
r中同名的監聽器,則兩個監聽器函數會以冒泡的方式先後執行。
父組件:
<template>
<div class="test">
<child v-bind="{name, sex, age}" v-on="{changeName,changeAge}"></child>
</div>
</template>
<script>
import child from './child'
export default {
data() {
return {
name: '張三',
sex: '男',
age: 11,
}
},
components: {
child
},
methods: {
changeName(name) {
this.name = name
},
changeAge(age) {
this.age = age
}
}
}
</script>
子組件child.vue
<template>
<div class="child">
child組件的$attrs {{$attrs}}
<child-child v-bind="$attrs" v-on="$listeners" @showAttrs="showAttrs"></child-child>
</div>
</template>
<script>
import childChild from './child-child'
export default {
name: "child",
props: ['name'],
inheritAttrs: false,
created() {
console.log('child', this.$listeners)
},
components: {
childChild
},
methods: {
showAttrs() {
console.log(this.$attrs)
}
}
}
</script>
孫子組件:child-child.vue
<template>
<div class="child-child">
child-child組件的$attrs {{$attrs}}
</div>
</template>
<script>
export default {
name: "child-child",
inheritAttrs: false,
created() {
console.log('child-child',this.$listeners)
}
}
</script>
打印結果: