當父組件向子組件傳遞屬性或監聽器,而這些屬性未在子組件中定義為props
或emits
時,它們會自動繼承並附加到子組件的根元素
上,常見的屬性包括class
、style
、id
等。這些屬性可以透過$attrs
屬性訪問並進行操作。
以class
為例,如果子組件的根元素已有樣式,傳入的class
屬性會與現有的樣式合併
,並一同應用,確保既有樣式與父組件傳遞的樣式同時生效。
⭐ 當組件的模板中只有一個元素作為頂層元素時,該元素會被稱作組件的根元素。
以下是一個子組件定義按鈕,並且由父組件區域註冊後,透傳class
樣式來渲染子組件的範例:
👉 Vue Options API Vue 父組件透傳屬性實作連結
父組件 Vue Template:
<div id="app">
<my-button-component class="bg-primary"></my-button-component>
</div>
子組件 Vue Template:
<template id="myButton">
<button class="text-white" type="button">我是按鈕</button>
</template>
⭐ 可以試著修改子組件<my-button-component>
的bg-primary
顏色,當子組件按鈕繼承這個樣式的時候,背景顏色也會跟著改變。
延續上面按鈕的案例,改成父組件傳遞監聽器至子組件透傳的方式:
👉 Vue Options API Vue 父組件透傳事件監聽器實作連結
父組件 Vue Template:
<div id="app">
<my-button-component @click="clickHandler" class="bg-primary"></my-button-component>
</div>
子組件 Vue Template:
<template id="myButton">
<button class="text-white" type="button">我是按鈕</button>
</template>
當子組件的按鈕被點擊的時候,會觸發父組件透傳的cliek
事件監聽器。但如果子組件的按鈕也設定click
事件監聽器的話,就會子組件的click
事件監聽器先觸發,接著觸發父組件的click
事件監聽器。
如果想看傳入的屬性有哪些,可以使用this.$attrs
印出查看:
⭐ $attrs
是非響應式資料,若需要是響應式資料可以透過props
的方式傳遞。
⭐ $attrs
物件會保留父組件傳入的原始屬性名稱,不會像props
那樣將kebab-case(串燒式)
的命名自動轉換為camelCase(駝峰式)
。然而,事件監聽器的命名會遵循JavaScript
的慣例,自動轉換為on
開頭加上大駝峰(PascalCase)
的格式(EX:模板上@click
,在$attrs
中看到名稱是onClick
)。
如果子組件使用單一根節點,但不希望自動繼承父組件傳遞的屬性,可以在子組件中設置inheritAttrs: false
來禁用屬性的自動繼承。
👉 Vue3 Options API Vue 父組件透傳監聽器(inheritAttrs: false)避免自動繼承實作連結
前面有提及若組件是單個根元素
組成,父元件可以透過屬性
或監聽器
透傳的方式,直接繼承在根元素上。但若是組件是多個根元素組成,則不會觸發自動繼承的行為(可以想成他不知道誰要繼承)。
當面對多根節點的組件,可搭配v-bind="$attrs"
的方式(全部屬性及監聽器都繼承),明確定義子組件要繼承屬性的根元素,使父組件的屬性能成功繼承到開發者指定的根元素上。
👉 Vue Options API Vue 父組件傳遞屬性給多個根節點子組件實作連結
流程說明:將父組件透傳的屬性,繼承在子組件【我是按鈕 2】的按鈕上。
子組件 Vue Template:
<template id="myButton">
<div class="button-wrapper">
<button class="text-white" type="button">我是按鈕 1</button>
</div>
<!-- 指定【我是按鈕 2】繼承父組件透傳屬性及監聽器 -->
<div class="button-wrapper">
<button class="text-white" v-bind="$attrs" type="button">我是按鈕 2</button>
</div>
<div class="button-wrapper">
<button class="text-white" type="button">我是按鈕 3</button>
</div>
</template>
⭐ 如果想要指定某些屬性或監聽器的繼承,需要手動綁定對應的屬性。例如,對於class
樣式,可以使用 :class="$attrs.class"
來進行明確綁定。
在組件嵌套的情況下,當父組件的屬性透傳到子組件時,如果某一層組件通過將這些屬性聲明為props
或通過 emits
進行消費,這些屬性將不會繼續向下一層傳遞。相反,若某些屬性未在該層組件中被顯式聲明,它們會保留在$attrs
中,並可以通過 $attrs 傳遞給更深層的子組件。
👉 Vue Options API Vue 父組件傳遞屬性給深層嵌套組件實作連結
流程說明:
button-class
屬性及click
事件監聽器到第一層子組件(myButton)props
,並使用class
綁定父組件透傳的buttonClass
屬性(可以點擊【我是按鈕3】,印出父組件傳入的屬性值)$attrs
接收未被myButton
消費的屬性,並綁定父組件透傳的click
事件監聽器父組件 Vue Template:
<div id="app">
<my-button-component @button-click="clickHandler" button-class="bg-primary"></my-button-component>
</div>
第一層子組件(mybutton) Vue Template:
<template id="myButton">
<h2>第一層子組件 - 我是 myButton 區域</h2>
<div class="button-wrapper">
<button class="text-white" type="button">我是按鈕 1</button>
</div>
<div class="button-wrapper">
<button class="text-white" type="button">我是按鈕 2</button>
</div>
<div class="button-wrapper">
<!-- 聲明 buttonClass 是 props 屬性 -->
<button class="text-white" :class="buttonClass" @click="getAttribute" type="button">我是按鈕 3</button>
</div>
<!-- 將剩餘未被聲明的屬性繼續往深層組件傳遞 -->
<base-button-component v-bind="$attrs"></base-button-component>
</template>
第二層子組件(baseButton) Vue Template:
<template id="baseButton">
<h2>第二層子組件 - 我是 baseButton 區域</h2>
<div class="button-wrapper">
<button class="text-white" type="button">我是按鈕 4</button>
</div>
<div class="button-wrapper">
<!-- 手動綁定從父組件透傳的事件監聽器(監聽器轉成 javascript 命名風格 onxxx) -->
<button @click="$attrs.onButtonClick" class="text-white" type="button">我是按鈕 5</button>
</div>
</template>
$attrs
訪問。而如果子組件包含多個根元素,這些屬性與監聽器將不會自動繼承,此時需要通過v-bind="$attrs"
將它們手動綁定到指定元素,或者分別手動綁定樣式與監聽器。屬性
和監聽器
,如果在中間層的子組件被聲明為props
或emit
,這些屬性會被消費
掉,無法繼續透傳到更深層的子組件。若未被消費
且未被使用的情況下,這些屬性可以繼續向更深層的子組件透傳。