---
# System prepended metadata

title: 【Vue】貫穿屬性(Fallthrough Attritube)
tags: [Vue]

---

###### tags: `Vue`

# 【Vue】貫穿屬性(Fallthrough Attritube)

## 屬性繼承

**fallthrough attritube** 指的是傳遞給元件的屬性或是 v-on 事件監聽器，但卻沒有透過 props 或 emit 做宣告，常見的例子有 `style`、`class`、`id` 的屬性
:::info
:pencil2: 看起來都是以 html 的屬性居多
:::

舉個例子，假設有個 MyButton 元件的模板如下
```html=
<button>click me</button>
```

另一個元件使用了這個 button 元件，並傳入 `class=large` 的屬性
```html=
<MyButton class="large" />
```

最後渲染出的 DOM
```html=
<button class="large">click me</button>
```

子元件不需要宣告 class 屬性也能使用父元件定義的 `class=large`，這裡的 `class` 就稱為 **fallthrough attribute**

### 對 `class` 與 `style` 的合併

如果子元件本來就有 class 屬性
```html=
<button class="btn">click me</button>
```

最後渲染出的 DOM
```html=
<button class="btn large">click me</button>
```
可以看見 class 屬性會合併，同樣的 style 也會自動合併

### `v-on` 監聽器繼承

`v-on` 監聽器也遵循著相同規則

父元件傳入 `v-on`
```html=
<MyButton @click="onClick" />
```
子元件不用做任何宣告，按下按鈕時也會觸發 onClick 函式

:::info
如果子元件本來就有寫 @click="handler"，那麼按下時會同時觸發兩個 handler
:::

**[playground](https://playcode.io/1487762)**

### 巢狀元件繼承

如果 MyButton 元件的模板也是使用別的元件

```html=
<BaseButton/>
```
通過使用 MyButton 的父元件傳遞過來的 fallthrough 屬性，同樣也會傳給 BaseButton 元件

:::warning
1. MyButton 宣告的 props、v-on 監聽器不會傳給 BaseButton
2. 從 MyButton 傳過去的屬性 BaseButton 也可以寫成用 props 來接收
:::

## 禁用屬性繼承
你如果不想讓元件繼承到父層的屬性，可以把 `inheritAttrs` 設定成 `false`

如果你使用 `<script setup>` 來撰寫，要另外寫一個 `<script>` 來設定
```html=
<script>
// use normal <script> to declare options
export default {
  inheritAttrs: false
}
</script>

<script setup>
// ...setup logic
</script>
```
一般來說會禁用屬性繼承的情況會發生在屬性需要應用在根節點以外的其他元素上，透過禁用 inheritAttrs 你可以完全自由的操控傳進來的屬性。

fallthrough 屬性可以直接透過 `$attrs` 在模板中做操作
```html=
<span>Fallthrough attributes: {{ $attrs }}</span>
```
:::warning
1. 不像 props，fallthrough 屬性會保留原本變數的大小寫樣式，所以假設有個屬性名稱叫做 `foo-bar`，那他就必須透過 `$attrs['foo-bar']` 來存取，不能使用 `$attrs['fooBar']`
2. `v-on` 事件監聽器(例如 @click) 會轉成一個函式( $attrs.onClick )
:::

有時候為了撰寫樣式我們會在原本的元素外再多包一層 div 
```html=
<div class="btn-wrapper">
  <button class="btn">click me</button>
</div>
```
但我們可能不希望透傳進來的參數套用到外層 div，此時可以透過 v-bind 來實現:
```html=
<div class="btn-wrapper">
  <button class="btn" v-bind="$attrs">click me</button>
</div>
```
:::warning
`v-bind` 如果沒有綁到指定的參數(ex: id)，預設會綁所有屬性到目標元素上
:::

## 多個根節點的屬性繼承

不像單個根節點，多個根節點的元件不會自動做參數透傳，如果沒有特別寫 v-bind 來綁定 $attrs 會產生 warning

[playground](https://playcode.io/1487766)

## 在 Javascript 中傳遞 fallthrough attritube

有需要的話可以透過在 `script setup` 中使用 `useAttrs()` 來傳遞

```html=
<script setup>
import { useAttrs } from 'vue'

const attrs = useAttrs()
</script>
```

在 SFC 以外的地方可以寫成

```js=
export default {
  setup(props, ctx) {
    // fallthrough attributes are exposed as ctx.attrs
    console.log(ctx.attrs)
  }
}
```

:::info
attrs 物件只會反應出當下最新的屬性，他並沒有響應性，所以你不能夠使用 watchers 來監控他的改變，如果你需要的是響應性請改用 props，又或者使用 `onUpdated()`來操作每次更新後的改變
:::