2024-10-02|閱讀時間 ‧ 約 22 分鐘

EP25 - 降級屬性

Fallthrough Attributes 感覺是一種特殊的屬性!
之前文章有提到過幾次了~要好好再研究清楚!
只是這屬性的英文應該是~fall throught 降級的意思?!!! 不是失敗的意思~

這個頁面假設您已經閱讀過「組件基礎」內容。對組件不熟悉,建議先閱讀該部分。

屬性繼承 - Attribute Inheritance

「降級屬性」是指傳遞給組件的屬性或 v-on 事件監聽器,但在接收組件的 props 或 emits 中沒有明確聲明。常見的例子包括 class、style 和 id 屬性。

當組件渲染一個單根元素時,降級屬性將自動添加到根元素的屬性中。例如,給定一個具有以下模板的 <MyButton> 組件:

<!-- <MyButton> 的模板 -->
<button>Click Me</button>

以及一個使用此組件的父組件:

<MyButton class="large" />

最終渲染的 DOM 將是:

<button class="large">Click Me</button>

在這裡,<MyButton> 沒有將 class 聲明為可接受的 prop。因此,class 被視為一個降級屬性,自動添加到 <MyButton> 的根元素中。

class 和 style 的合併 -class and style Merging

如果子組件的根元素已經有現有的 class 或 style 屬性,這些屬性將與從父組件繼承的 class 和 style 值合併。假設我們將前一個例子中 <MyButton> 的模板更改為:

<!-- <MyButton> 的模板 -->
<button class="btn">Click Me</button>

那麼最終渲染的 DOM 將變為:

<button class="btn large">Click Me</button>

v-on 監聽器繼承 - v-on Listener Inheritance

相同的規則適用於 v-on 事件監聽器:

<MyButton @click="onClick" />

click 監聽器將被添加到 <MyButton> 的根元素,即原生 <button> 元素上。當原生 <button> 被點擊時,將觸發父組件的 onClick 方法。如果原生 <button> 已經綁定了一個使用 v-on 的 click 監聽器,那麼兩個監聽器都會被觸發。

嵌套組件繼承 - Nested Component Inheritance

如果一個組件渲染另一個組件作為其根節點,例如,我們重構了 <MyButton> 以渲染 <BaseButton> 作為其根:

<!-- 渲染另一個組件的 <MyButton/> 的模板 -->
<BaseButton />

那麼 <MyButton> 接收到的降級屬性將自動轉發給 <BaseButton>

注意:

  • 被轉發的屬性不包括被聲明為 props 的任何屬性,或由 <MyButton> 聲明的事件的 v-on 監聽器——換句話說,聲明的 props 和監聽器已被 <MyButton>「消耗」。
  • 被轉發的屬性如果被 <BaseButton> 聲明,則可以作為 props 接受。

禁用屬性繼承 - Disabling Attribute Inheritance

如果您不希望組件自動繼承屬性,可以在組件選項中設置 inheritAttrs: false

自 3.3 版本以來,您也可以在 <script setup> 中直接使用 defineOptions

<script setup>
defineOptions({
inheritAttrs: false
})
// ...setup 邏輯
</script>

禁用屬性繼承的常見情境是當屬性需要應用於根節點之外的其他元素時。通過將 inheritAttrs 選項設置為 false,您可以完全控制降級屬性應該應用的位置。

這些降級屬性可以通過 $attrs 在模板表達式中直接訪問:

<span>降級屬性: {{ $attrs }}</span>

$attrs 對象包括所有未由組件的 props 或 emits 選項聲明的屬性(例如 class、style、v-on 監聽器等)。

一些注意事項:

  • 與 props 不同,降級屬性在 JavaScript 中保留其原始大小寫,因此像 foo-bar 這樣的屬性需要作為 $attrs['foo-bar'] 訪問。
  • @click 這樣的 v-on 事件監聽器將作為一個函數暴露在 $attrs 下。

使用我們在前一部分的 <MyButton> 組件示例——有時我們可能需要為樣式目的將實際的 <button> 元素包裹在額外的 <div> 中:

<div class="btn-wrapper">
<button class="btn">Click Me</button>
</div>

我們希望所有降級屬性(如 class 和 v-on 監聽器)應用於內部的 <button>,而不是外部的 <div>。我們可以通過設置 inheritAttrs: falsev-bind="$attrs" 來實現:

<div class="btn-wrapper">
<button class="btn" v-bind="$attrs">Click Me</button>
</div>

請記住,沒有參數v-bind 將對象的所有屬性綁定為目標元素的屬性。

多根節點上的屬性繼承 - Attribute Inheritance on Multiple Root Nodes

與單根節點的組件不同,具有多根節點的組件沒有自動的屬性降級行為。如果 $attrs 沒有明確綁定,將會發出運行時警告。

<CustomLayout id="custom-layout" @click="changeValue" />

如果 <CustomLayout> 有以下多根模板,將會發出警告,因為 Vue 無法確定將降級屬性應用到哪裡:

<header>...</header>
<main>...</main>
<footer>...</footer>

如果明確綁定了 $attrs,則警告將被抑制:

<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>

在 JavaScript 中訪問降級屬性 - Accessing Fallthrough Attributes in JavaScript

如果需要,您可以使用 useAttrs() API 在 <script setup> 中訪問組件的降級屬性:

<script setup>
import { useAttrs } from 'vue'

const attrs = useAttrs()
</script>

如果不使用 <script setup>,則 attrs 將作為 setup() 上下文的屬性暴露:

export default {
setup(props, ctx) {
// 降級屬性作為 ctx.attrs 暴露
console.log(ctx.attrs)
}
}

注意,雖然這裡的 attrs 對象始終反映最新的降級屬性,但它不是響應式的(出於性能考慮)。您無法使用監視器來觀察其變化。如果您需要響應性,請使用 prop。或者,您可以使用 onUpdated() 在每次更新時執行副作用。

為什麼看官方文件總有一種不知道他在幹嘛的感覺?
因為繼承放在屬性裡頭真的要多花一點時間理解~
組件之間屬性的關係蠻微妙的~
分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.