隔山打牛的傳透 Attributes

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()。

Leave a Reply

Your email address will not be published. Required fields are marked *