iT邦幫忙

2024 iThome 鐵人賽

DAY 21
1
Modern Web

Vue 3 初學者:用實作帶你看過核心概念系列 第 21

Vue 3 用實作帶你看過核心概念 - Day 21: 組件透傳屬性(Attributes)的應用

  • 分享至 

  • xImage
  •  

文章背景圖

目錄

  • 單根節點透傳屬性基礎應用
  • 多根節點屬性繼承方式
  • 深層子組件透傳方式
  • 總結

單根節點透傳屬性基礎應用

當父組件向子組件傳遞屬性或監聽器,而這些屬性未在子組件中定義為propsemits時,它們會自動繼承並附加到子組件的根元素上,常見的屬性包括classstyleid等。這些屬性可以透過$attrs屬性訪問並進行操作。

class為例,如果子組件的根元素已有樣式,傳入的class屬性會與現有的樣式合併,並一同應用,確保既有樣式與父組件傳遞的樣式同時生效。

⭐ 當組件的模板中只有一個元素作為頂層元素時,該元素會被稱作組件的根元素。

以下是一個子組件定義按鈕,並且由父組件區域註冊後,透傳class樣式來渲染子組件的範例:

👉 Vue Options API Vue 父組件透傳屬性實作連結

父組件透傳 class 樣式

父組件 Vue Template:

<div id="app">
  <my-button-component class="bg-primary"></my-button-component>
</div>

子組件 Vue Template:

<template id="myButton">
  <button class="text-white" type="button">我是按鈕</button>
</template>

⭐ 可以試著修改子組件<my-button-component>bg-primary顏色,當子組件按鈕繼承這個樣式的時候,背景顏色也會跟著改變。


延續上面按鈕的案例,改成父組件傳遞監聽器至子組件透傳的方式:

👉 Vue Options API Vue 父組件透傳事件監聽器實作連結

父組件 Vue Template:

<div id="app">
  <my-button-component @click="clickHandler" class="bg-primary"></my-button-component>
</div>

子組件 Vue Template:

<template id="myButton">
  <button class="text-white" type="button">我是按鈕</button>
</template>

當子組件的按鈕被點擊的時候,會觸發父組件透傳的cliek事件監聽器。但如果子組件的按鈕也設定click事件監聽器的話,就會子組件的click事件監聽器先觸發,接著觸發父組件的click事件監聽器。

如果想看傳入的屬性有哪些,可以使用this.$attrs印出查看:
console.log 印出 $attrs 屬性

$attrs是非響應式資料,若需要是響應式資料可以透過props的方式傳遞。
$attrs物件會保留父組件傳入的原始屬性名稱,不會像props那樣將kebab-case(串燒式)的命名自動轉換為camelCase(駝峰式)。然而,事件監聽器的命名會遵循JavaScript的慣例,自動轉換為on開頭加上大駝峰(PascalCase)的格式(EX:模板上@click ,在$attrs中看到名稱是onClick)。


如果子組件使用單一根節點,但不希望自動繼承父組件傳遞的屬性,可以在子組件中設置inheritAttrs: false來禁用屬性的自動繼承。

👉 Vue3 Options API Vue 父組件透傳監聽器(inheritAttrs: false)避免自動繼承實作連結

多根節點屬性繼承方式

前面有提及若組件是單個根元素組成,父元件可以透過屬性監聽器透傳的方式,直接繼承在根元素上。但若是組件是多個根元素組成,則不會觸發自動繼承的行為(可以想成他不知道誰要繼承)。

當面對多根節點的組件,可搭配v-bind="$attrs"的方式(全部屬性及監聽器都繼承),明確定義子組件要繼承屬性的根元素,使父組件的屬性能成功繼承到開發者指定的根元素上。

👉 Vue Options API Vue 父組件傳遞屬性給多個根節點子組件實作連結

流程說明:將父組件透傳的屬性,繼承在子組件【我是按鈕 2】的按鈕上。

多根節點元素透傳繼承範例展示圖

子組件 Vue Template:

<template id="myButton">
  <div class="button-wrapper">
    <button class="text-white" type="button">我是按鈕 1</button>
  </div>
  <!--   指定【我是按鈕 2】繼承父組件透傳屬性及監聽器 -->
  <div class="button-wrapper">
    <button class="text-white" v-bind="$attrs" type="button">我是按鈕 2</button>
  </div>
  <div class="button-wrapper">
    <button class="text-white" type="button">我是按鈕 3</button>
  </div>
</template>

⭐ 如果想要指定某些屬性或監聽器的繼承,需要手動綁定對應的屬性。例如,對於class樣式,可以使用 :class="$attrs.class"來進行明確綁定。

深層子組件透傳方式

在組件嵌套的情況下,當父組件的屬性透傳到子組件時,如果某一層組件通過將這些屬性聲明為props或通過 emits進行消費,這些屬性將不會繼續向下一層傳遞。相反,若某些屬性未在該層組件中被顯式聲明,它們會保留在$attrs中,並可以通過 $attrs 傳遞給更深層的子組件。

深層組件屬性透傳範例流程圖

👉 Vue Options API Vue 父組件傳遞屬性給深層嵌套組件實作連結

流程說明:

  1. 父組件傳遞button-class屬性及click事件監聽器到第一層子組件(myButton)
  2. 第一層子組件(myButton)顯式聲明props,並使用class綁定父組件透傳的buttonClass屬性(可以點擊【我是按鈕3】,印出父組件傳入的屬性值)
  3. 第二層子組件(baseButton)通過$attrs接收未被myButton消費的屬性,並綁定父組件透傳的click事件監聽器

父組件 Vue Template:

<div id="app">
  <my-button-component @button-click="clickHandler" button-class="bg-primary"></my-button-component>
</div>

第一層子組件(mybutton) Vue Template:

<template id="myButton">
  <h2>第一層子組件 - 我是 myButton 區域</h2>
  <div class="button-wrapper">
    <button class="text-white" type="button">我是按鈕 1</button>
  </div>
  <div class="button-wrapper">
    <button class="text-white" type="button">我是按鈕 2</button>
  </div>
  <div class="button-wrapper">
    <!--     聲明 buttonClass 是 props 屬性 -->
    <button class="text-white" :class="buttonClass"  @click="getAttribute" type="button">我是按鈕 3</button>
  </div>
  <!--   將剩餘未被聲明的屬性繼續往深層組件傳遞 -->
  <base-button-component v-bind="$attrs"></base-button-component>
</template>

第二層子組件(baseButton) Vue Template:

<template id="baseButton">
  <h2>第二層子組件 - 我是 baseButton 區域</h2>
  <div class="button-wrapper">
    <button class="text-white" type="button">我是按鈕 4</button>
  </div>
  <div class="button-wrapper">
    <!--     手動綁定從父組件透傳的事件監聽器(監聽器轉成 javascript 命名風格 onxxx) -->
    <button @click="$attrs.onButtonClick" class="text-white" type="button">我是按鈕 5</button>
  </div>
</template>

總結

  • 父組件透傳屬性及監聽器:當子組件只有單一根元素時,父組件傳遞的屬性與事件監聽器會自動繼承並應用到子組件的根元素上,這些屬性可以透過$attrs訪問。而如果子組件包含多個根元素,這些屬性與監聽器將不會自動繼承,此時需要通過v-bind="$attrs"將它們手動綁定到指定元素,或者分別手動綁定樣式與監聽器。
  • 深層子組件的屬性透傳:父組件透傳的屬性監聽器,如果在中間層的子組件被聲明為propsemit,這些屬性會被消費掉,無法繼續透傳到更深層的子組件。若未被消費且未被使用的情況下,這些屬性可以繼續向更深層的子組件透傳。

上一篇
Vue 3 用實作帶你看過核心概念 - Day 20:組件使用 v-model 雙向綁定
下一篇
Vue 3 用實作帶你看過核心概念 - Day 22:v-slot 插槽使用
系列文
Vue 3 初學者:用實作帶你看過核心概念30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言