Vue中的 $attrs 和 $listeners
最近在研究 Vue 的組件庫,之前也用過 $attrs 和 $listeners,官方文檔描述的不太詳細,也沒有太好的例子,就沒有深入的研究過這兩個屬性。最近生病在家,正好有時間好好研究一下 Vue 的高階用法,寫了幾個 demo,下面我們來看看這兩個屬性到底有什么奧秘。
$attrs
我們先來看看官方文檔的 api 描述是怎樣描述 $attrs 的
乍一看可能有點懵,下面我們結合下面的例子來看看 $attrs 的作用
普通的 props 傳值
先來看看下面的例子,我自己寫了一個雙向綁定的 input 的組件,來驗證一下。
<!-- 父組件 -->
<mxx-input v-model="user.username" type="text" foo="foo"></mxx-input>
<!-- 子組件 -->
<div>
<input v-bind="$attrs" :type="type" :value="value">
</div>
// 子組件
export default {
name: 'MxxInput',
props: {
type: {
type: String,
default: 'text'
},
value: {
type: String,
}
},
}
我們來看看子組件渲染的 DOM 結構
我們會發現 foo 這個屬性加在了 MxxInput 組件的最外層。
如果你仔細看過 Vue 的官方文檔的話,你會發現我們在使用 props 的方式向子組件傳值的時候,子組件沒有使用 props 作為接受的話,那么這個屬性會自動設置在子組件的最外層的 HTML 標簽上。如果是 class 和 style 的話,會合並最外層標簽的 class 和 style。
$attrs 的作用
如果子組件中不想繼承父組件傳入的非 prop 屬性,可以使用 inheritAttrs 禁用繼承,然后通過 v-bind="$attrs" 把外部傳入的非 prop 屬性設置給希望的標簽上。
也就是你不想將沒有設置 props 的屬性自動繼承到組件最外層的標簽上那么你就需要將 inheritAttrs 這個屬性設置為 false,但是這不會改變 class 和 style。
// 子組件
export default {
name: 'MxxInput',
inheritAttrs: false,
props: {
type: {
type: String,
default: 'text'
},
value: {
type: String,
}
},
}
我們再來看看新的 DOM 結構
我們可以看到最外層的 DOM 結構上沒有 foo 這個屬性了!
而 $attrs 的值就是 { "foo": "foo" },這樣我們就可以利用這個特性實現組件的屬性透傳,更多有趣的用法,你可以自己去摸索一下。
$listeners
普通 $emit 傳值
我們先來看看如果我們想從一個組件向組件外傳值,一種方法就是使用 $emit 向組件外暴露一個事件,然后通過事件方法的參數傳值。
<input
type="text"
v-bind="$attrs"
@focus="$emit('focus', $event)"
@input="$emit('input', $event)"
>
$listeners 的作用
其實 $attrs 的一個作用就是可以批量向組件內傳值,但是如果我們想批量向組件外傳值怎么辦呢,這個時候我們就可以使用 $listeners
<input
type="text"
v-bind="$attrs"
v-on="$listeners"
>
$listeners 實際就相當於上面的多個 $emit 了