VUE 官網把 Fallthrough Attributes 翻為 「透传 Attributes」,聽起來有點莫測高深,說穿了其實滿好懂的。簡單來講,如果在父層置入子元件,同時在父層的子元件上加上 class 樣式,則這個 class 樣式會傳入子元件與子元件內 DOM 的原本 class 樣式結合。
舉官網的例子說明,如我們在 Parent.vue 置入 <MyButton />,同時在 <MyButton />上綁上 class=”red”,則這個 class=”red” 會傳入 <MyButton /> 內的 <button> 元素,並與 <button> 元素本來的樣式 bold 結合。
Parent.vue
<script setup>
</script>
<template>
<MyButton class="red" />
</template>
<style>
.red {
color: red;
}
</style>
MyButton.vue
<script setup></script>
<template>
<button class="bold">click me</button>
</template>
<style>
.bold {
font-weight: 900;
}
</style>如果是槽狀的元件,最底層的元件會接收到上層所有綁定的 class 樣式
App.vue
<script setup>
import { ref } from 'vue'
import MyButton from './MyButton.vue'
const msg = ref('Hello World!')
</script>
<template>
<MyButton class="red" />
</template>
<style>
.red {
color: red;
}
</style>MyButton.vue
<script setup>
import BaseButton from './BaseButton.vue'
</script>
<template>
<BaseButton class="bold">click me</BaseButton>
</template>
<style>
.bold {
font-weight: 900;
}
</style>BaseButton.vue
<script setup></script>
<template>
<button class="bg-yellow">click me</button>
</template>
<style>
.bg-yellow {
background: yellow;
}
</style>
如上截圖,render 出來的 DOM 結合了來自 App.vue 的 .red 樣式、MyButton.vue 的 .bold 樣式還有BaseButton.vue 自己的 .bg-yellow 樣式。
事件的傳透
不只是樣式,事件的綁定也有傳透的效果,如下,在 App.vue 中置入 MyButton.vue,在 MyButton.vue 中置入 BaseButton.vue,每一層中各自綁定 click 事件:
App.vue
<script setup>
import { ref } from 'vue'
import MyButton from './MyButton.vue'
function appClick(){
alert('App')
}
</script>
<template>
<MyButton class="red" @click="appClick"/>
</template>
MyButton.vue
<script setup>
import BaseButton from './BaseButton.vue'
function myClick(){
alert('MyButton')
}
</script>
<template>
<BaseButton class="bold" @click="myClick">click me</BaseButton>
</template>
BaseButton.vue
<script setup>
function baseClick() {
alert('BaseButton')
}
</script>
<template>
<button class="bg-yellow" @click="baseClick">click me</button>
</template>
點擊元件時會從最底層的元件依序往上觸發每一層各自的事件。

承上,如果希望最底層的元件不要繼承來自上層的樣式或是事件,可以加上:
defineOptions({
inheritAttrs: false
})
<script setup>
function baseClick() {
alert('BaseButton')
}
defineOptions({
inheritAttrs: false
})
</script>
<template>
<button class="bg-yellow" @click="baseClick">click me</button>
</template>
如此,則最底層的元件不會繼承來自上層的樣式與事件。
在最底層使用 {{ $attrs }} 訪問到傳透進來的樣式與事件。
<span>Fallthrough attribute: {{ $attrs }}</span>

<script setup>
function baseClick() {
alert('BaseButton')
}
defineOptions({
inheritAttrs: false
})
</script>
<template>
<span>Fallthrough attribute: {{ $attrs }}</span>
<br>
<button class="bg-yellow" @click="baseClick">click me</button>
</template>
<style>
.bg-yellow {
background: yellow;
}
</style>
如果在最底層的 BaseButton.vue 的 button 外面再包一層 div,則 button 並不會繼承來自上層的樣式,要先加上:
defineOptions({
inheritAttrs: false
})
另外在 button 上面綁定 v-bind=”$attrs”,button 才能繼承到上層的樣式及事件:
<button class="bg-yellow" @click="baseClick" v-bind="$attrs">click me</button>
<script setup>
function baseClick() {
alert('BaseButton')
}
defineOptions({
inheritAttrs: false
})
</script>
<template>
<span>Fallthrough attribute: {{ $attrs }}</span>
<br>
<div class="btn-wrapper">
<button class="bg-yellow" @click="baseClick" v-bind="$attrs">click me</button>
</div>
</template>
<style>
.bg-yellow {
background: yellow;
}
</style>
最後,要注意 attrs 並不是響應式的,所以無法透過 watch 去監聽它的變化,如果需要及時監聽,則要使用prop,或是使用 onUpdated()。
