跳轉到內容

穿透屬性

本頁假設您已經閱讀了元件基礎。如果是元件新手,請先閱讀該內容。

屬性繼承

“穿透屬性”是指傳遞給元件但未在接收元件的propsemits中顯式宣告的屬性或v-on事件監聽器。常見示例包括classstyleid屬性。

當一個元件渲染單個根元素時,穿透屬性將自動新增到根元素的屬性中。例如,給定一個具有以下模板的<MyButton>元件

template
<!-- template of <MyButton> -->
<button>Click Me</button>

並且一個父元件使用此元件

template
<MyButton class="large" />

最終的渲染DOM將是

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

在這裡,<MyButton>未宣告class為接受的prop。因此,class被視為穿透屬性並自動新增到<MyButton>的根元素。

classstyle合併

如果子元件的根元素已經存在 classstyle 屬性,它們將與從父元件繼承的 classstyle 值合併。假設我們將上一個示例中的 <MyButton> 模板進行修改

template
<!-- template of <MyButton> -->
<button class="btn">Click Me</button>

那麼最終渲染的 DOM 將變為

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

v-on 監聽器繼承

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

template
<MyButton @click="onClick" />

click 監聽器新增到 <MyButton> 的根元素上,即原生的 <button> 元素。當原生 <button> 被點選時,它將觸發父元件的 onClick 方法。如果原生 <button> 已經透過 v-on 綁定了 click 監聽器,則兩個監聽器都將被觸發。

巢狀元件繼承

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

template
<!-- template of <MyButton/> that simply renders another component -->
<BaseButton />

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

注意

  1. 透傳屬性不包括任何被宣告為 props 或由 <MyButton> 宣告的事件的 v-on 監聽器——換句話說,宣告的 props 和監聽器已經被 <MyButton> “消費”了。

  2. 如果 <BaseButton> 宣告,則可以將其作為 props 接受透傳屬性。

停用屬性繼承

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

從 3.3 版本開始,您也可以在 <script setup> 中直接使用 defineOptions

vue
<script setup>
defineOptions({
  inheritAttrs: false
})
// ...setup logic
</script>

停用屬性繼承的常見場景是當需要將屬性應用於根節點以外的其他元素時。透過將 inheritAttrs 選項設定為 false,您可以完全控制透傳屬性應該應用的位置。

這些透傳屬性可以直接在模板表示式中作為 $attrs 訪問

template
<span>Fallthrough attributes: {{ $attrs }}</span>

$attrs 物件包括所有未由元件的 propsemits 選項宣告的屬性(例如,classstylev-on 監聽器等)。

一些注意事項

  • 與 props 不同,透傳屬性在 JavaScript 中保留了它們的原始大小寫,因此像 foo-bar 這樣的屬性需要按 $attrs['foo-bar'] 訪問。

  • @click 這樣的 v-on 事件監聽器將在物件上作為 $attrs.onClick 下的函式暴露。

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

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

我們希望所有像 classv-on 監聽器這樣的透傳屬性都應用於內層的 <button>,而不是外層的 <div>。我們可以透過設定 inheritAttrs: falsev-bind="$attrs" 來實現這一點

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

請記住,v-bind 沒有引數 將物件的所有屬性繫結為目標元素的屬性。

多根節點上的屬性繼承

與單根節點的元件不同,多根節點的元件沒有自動屬性穿透行為。如果未顯式繫結 $attrs,則會發出執行時警告。

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

如果 <CustomLayout> 有以下多根模板,將會有警告,因為 Vue 無法確定應用屬性穿透的位置

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

如果顯式繫結 $attrs,則警告將被抑制

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

在 JavaScript 中訪問屬性穿透

如果需要,您可以在 <script setup> 中使用 useAttrs() API 訪問元件的屬性穿透

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

const attrs = useAttrs()
</script>

如果不使用 <script setup>,則 attrs 將作為 setup() 上下文的一個屬性公開

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

請注意,儘管這裡的 attrs 物件總是反映最新的屬性穿透,但它不是響應式的(出於效能原因)。您不能使用觀察者來觀察其變化。如果您需要響應式,請使用 prop。或者,您可以使用 onUpdated() 在每次更新時使用最新的 attrs 執行副作用。

如果需要,您可以透過 $attrs 例項屬性訪問元件的屬性穿透

js
export default {
  created() {
    console.log(this.$attrs)
  }
}
屬性穿透已載入