iT邦幫忙

2024 iThome 鐵人賽

DAY 22
1
Modern Web

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

Vue 3 用實作帶你看過核心概念 - Day 22:v-slot 插槽使用

  • 分享至 

  • xImage
  •  

文章背景圖

目錄

  • slot 默認內容插槽
  • slot 具名插槽
  • 子組件插槽傳遞參數 - 作用域插槽
  • slot 與 props 設計方式比較 - 以切換卡片展示型態為例
  • 條件插槽 - $slots
  • 動態插槽
  • 總結
  • 小試身手

slot 默認內容插槽

先前Day 18中提到父組件可以通過props將參數資料傳遞給子組件進行渲染,這樣可以有效地將資料處理與畫面渲染的工作分開。然而,當我們需要更加客製化的呈現內容,由父組件來控制子組件的部分顯示時,可以使用v-slot 指令,將父組件的模板傳入給子組件,可以更靈活地呈現內容。

v-slot 指令基本應用區域官方範例圖

以下透過一個父組件傳入按鈕模板內容給子組件的案例進行說明:

👉 Vue3 Options API slot 默認插槽內容實作連結

流程說明:

  1. 父組件區域註冊子組件。
  2. 在子組件使用的標籤內放入父組件要傳入的模板內容。
  3. 子組件使用slot標籤接應父組件要傳入的模板內容。

父組件 Vue Template:

<div id="app">
  <fancy-button>
    <button type="button">{{ icon }} {{ buttonText }}</button>
  </fancy-button>
</div>

父組件 javascript:

const rootComponent = {
  data() {
    return {
      buttonText: "我是按鈕",
      icon: "🐱"
    };
  },
  components: { FancyButton }
};

子組件 Vue Template:

<slot>
  <p>目前暫時不服務!</p>
</slot>

⭐ 子組件的slot標籤裡面可以放置預設值,當父組件未傳入模板內容的時候就會套用<p>目前暫時不服務!</p>。若父組件有傳入模板內容,則會忽略slot標籤裡面的內容。

這邊附上父組件透過props傳入資料,子組件根據資料渲染的案例,讓大家感受一下兩者架構的差異,也可以思考一下再擴增功能上的方向。
👉 Vue3 Options API 父組件傳遞 props 資料給子組件實作連結

slot 具名插槽

子組件使用的slot標籤可以設定不只一個,當使用複數個slot標籤的時候,父組件需要搭配具名插槽加上template標籤使用,才可以明確配對子組件中每個slot標籤要寫入對應的template是哪一個。

在說明v-slot縮寫之前,幫大家複習一下之前常用指令的縮寫:

  • v-bind:縮寫型態:
  • v-on:縮寫型態@
  • v-slot:縮寫型態#

slot 具名插槽對應模板名稱官方範例圖

以下透過子組件制定網頁版型,並由父組件根據不同區塊傳入模板內容案例說明:

👉 Vue3 Options API slot 具名插槽實作連結

流程說明:

  1. 子組件定出版型架構headermainfooter標籤區域。
  2. 父組件根據不同區域將對應該顯示的模板內容傳入。
  3. 透過template name與具名插槽名稱的對應,使彼此內容互不影響。

父組件 Vue Template:

<div id="app">
  <base-layout>
      
    <template v-slot:header>
      <h2>這是 header</h2>
    </template>
      
    <template #main>
      <h2>這是 main</h2>
    </template>
      
    <template #footer>
      <h2>這是 footer</h2>
    </template>
      
  </base-layout>
</div>

子組件 Vue Template

<template id="child">
  
  <header>
    <slot name="header"></slot>
  </header>
  
  <main>
    <slot name="main"></slot>
  </main>
  
  <footer>
    <slot name="footer"></slot>
  </footer>
  
</template>

如果slot標籤未設置名稱,或者使用了預設名稱default,那麼所有無法對應到具名插槽的模板內容都會被放置在這個插槽中。可以將slot標籤想像成一個分類器,負責將父組件傳入的模板內容進行分類。

延續上面的案例,我們試著透過範例更加了解:

👉 Vue3 Options API slot 未命名內容插槽與具名插槽的使用實作連結

父組件 Vue Template:

<div id="app">
  <base-layout>

    <template v-slot:header>
      <h2>這是 header</h2>
    </template>

    <template #main>
      <h2>這是 main</h2>
    </template>

    <template #footer>
      <h2>這是 footer</h2>
    </template>

    <div #test1122334455>123</div>
    <p>我是剩餘段落</p>

  </base-layout>
</div>

子組件 Vue Template:

<template id="child">

  <header>
    <slot name="header"></slot>
  </header>

  <main>
    <slot name="main"></slot>
  </main>

  <footer>
    <slot name="footer"></slot>
  </footer>

  <div class="container">
    <h2>未命名插槽內容(預設插槽)</h2>
    <slot></slot>
    <h2>具名的預設插槽內容(插槽名稱為 "default")</h2>
    <slot name="default"></slot>
  </div>
</template>

在這裡可以看到,使用#test1122334455這個具名插槽,但在子組件中沒有對應的插槽,因此其內容會自動放置到未命名的<slot></slot>或具名為default的插槽中。

子組件插槽傳遞參數 - 作用域插槽

一般來說,使用slot插槽時,父組件會傳遞模板內容給子組件來進行渲染。然而,在某些情況下,我們可能希望部分數據的控管在子組件中進行,並將這些參數回傳給父組件,使父組件能根據這些參數動態改變傳入的模板內容。這時,我們會使用作用域插槽,使子組件能將必要的參數提供給父組件使用。

此外,根據是否使用具名插槽,插槽的使用方式會有所不同。以下將分別舉例,幫助大家更好地理解作用域插槽的應用。

  • 預設內容插槽傳遞參數的情形:子組件通過插槽傳遞的所有內容,會被包裝在父組件使用 v-slot 定義的變數中(例如:<my-component v-slot="slotProps">

slot 作用域插槽官方範例圖

👉 Vue3 Options API 子組件默認內容插槽傳遞參數實作連結

父組件 Vue Template:

<div id="app">
  <my-component v-slot="slotProps">
    <p>slotProps:{{ slotProps }}</p>
    <p>text:{{ slotProps.text }}</p>
    <p>count:{{ slotProps.count }}</p>
  </my-component>
  <p>子組件外部範圍 slotProps:{{ slotProps }}</p>
</div>

⭐ 子組件通過v-slot傳遞的slotProps參數只能在父組件中引用插槽內容的區域內使用,而不能在父組件的外部範圍使用。

子組件 Vue Template:

<template id="child">
  <slot :text="text" :count="count"></slot>
</template>
  • 具名插槽傳遞參數的情況:子組件傳遞的參數必須對應到父組件中的具名<template>,否則參數無法正確傳遞與使用。在傳遞參數時,具名插槽會將其對應於特定的<template>,而且這些參數的作用域僅限於對應的<template>標籤內。

👉 Vue3 Options API slot 具名插槽傳值實作連結

父組件 Vue Template:

<div id="app">
  <base-layout>

    <template #header="slotProps">
      <h2>這是 header</h2>
      <p>查看子組件傳入的參數值:{{ slotProps }}</p>
    </template>

    <template #main="slotProps">
      <h2>這是 main</h2>
      <p>查看子組件傳入的參數值:{{ slotProps }}</p>
    </template>

    <template #footer="slotProps">
      <h2>這是 footer</h2>
      <p>查看子組件傳入的參數值:{{ slotProps }}</p>
    </template>

  </base-layout>
</div>

子組件 Vue Template:

<template id="child">

  <header>
    <slot name="header" :headeMessage="headeMessage"></slot>
  </header>

  <main>
    <slot name="main" :mainMessage="mainMessage"></slot>
  </main>

  <footer>
    <slot name="footer" :footerMessage="footerMessage"></slot>
  </footer>

</template>

在混合上面預設內容插槽傳遞參數具名插槽傳遞參數的情況下,預設插槽的內容需要在父組件中使用具名為default<template>才能正常編譯和顯示。

👉 Vue3 Options API slot 具名插槽 + 默認內容插槽傳值實作連結

slot 與 props 設計方式比較 - 以切換卡片展示型態為例

這裡我們以一個常見的模板類型為例,演示如何將內容以「列表式」或「卡片式」兩種方式進行呈現。我們將以 momo 官方網站為範例,並對比使用propsslot的組件設計思維,展示不同的實現方式。

【 momo 官網查詢 - 卡片式呈現搜尋結果 】
momo 官網查詢 - 卡片式呈現搜尋結果

【 momo 官網查詢 - 列表式呈現搜尋結果 】
momo 官網查詢 - 列表式呈現搜尋結果

  • props 的設計方式
    • 父組件管控要呈現的類別(showType)
    • 子組件負責取得查詢結果的 API 資料及根據父組件傳入的showType props決定呈現類型的組件。

👉 Vue3 Options API 不同排版方式 porps 參數設計方式實作連結
props 設計卡片式及列表式資料傳遞結構圖

  • slot 的設計方式
    • 父組件負責控制顯示類型(showType),並根據這個響應式變數決定渲染卡片式或列表式的組件模板內容。渲染所需的數據由子組件通過slot回傳的參數提供。
    • 子組件負責取得查詢結果的 API 資料並透過slot將參數回傳給父組件使用。

👉 Vue3 Options API 不同排版方式 slot 參數設計方式實作連結
slot 設計卡片式及列表式資料傳遞結構圖

兩種方式都能實現相同的功能,主要差異在於當顯示類型需要增加時(例如:新增 grid「四列網格」項目組件),兩種設計方式的修改如下:

  1. props 參數設計方式:
    • 在父組件的showType中新增第三種類型grid
    • 在子組件的邏輯判斷中加入新的showType選項grid
    • 子組件需新增「四列網格項目組件」以顯示相應的呈現方式。
  2. slot 參數設計方式:
    • 在父組件的showType中新增第三種類型grid
    • 在父組件的邏輯判斷中加入新的showType選項grid
    • 父組件需新增「四列網格項目組件」以顯示對應的模板內容

在這個場景案例中,使用props設計方式時,如果需要新增顯示類型,父組件和子組件都需要進行相應的調整。而使用slot設計方式時,子組件的職責更加單一,只負責處理API資料的讀取,因此僅需在父組件中進行修改,讓維護和擴展更為方便。

條件插槽 - $slots

在以下示例中,子組件的模板可以使用$slots配合條件語句來判斷插槽是否存在。如果插槽存在,則顯示父組件傳入的模板內容;如果插槽不存在,則顯示一條提示信息:<h2>父組件尚未提供 v-slot 綁定 content</h2>。此外,也可以在mounted生命週期階段通過console.log(this.$slots)來查看當前傳入的具名插槽。

👉 Vue3 Options API slot 條件渲染實作連結

父組件 Vue Template:

<div id="app">
  <child-component>
    <template #content>
      hello world vue !
    </template>
  </child-component>
</div>

子組件 Vue Template:

<template id="childComponent">
  <div v-if="$slots.content" class="content">
    <h2>
      <slot name="content"></slot>
    </h2>
  </div>
  <div v-else class="default">
    <h2>父組件尚未提供 v-slot 綁定 content</h2>
  </div>
</template>

動態插槽

v-bind可以用來設置動態屬性名稱,而v-slot同樣可以綁定動態插槽名稱,從而更靈活地控制插槽的內容。

透過以下案例進行流程說明:

  1. 下拉式選單透過v-model綁定blocktype響應式變數
  2. 根據這個響應式變數可以動態改動要渲染的插槽對象

👉 Vue3 Options API slot 動態插槽實作連結

父組件 Vue Template:

<div id="app">
    
  <div class="input-group">
    <label for="blockOption">選擇 slot 插槽名稱:</label>
    <select name="blockOption" id="blockOption" v-model="blocktype">
      <option disabled value="">請選擇</option>
      <option value="header">header</option>
      <option value="body">body</option>
      <option value="footer">footer</option>
    </select>
  </div>

  <div class="content">
    <child-component>
      <template #[blocktype]>
        <h2>今天開心嗎!!!!!(動態小壞蛋) 顯示 blocktype:{{ blocktype }}</h2>
      </template>
    </child-component>
  </div>
  
</div>

子組件 Vue Template:

<template id="childComponent">
  <div>我是childComponent!!</div>
  <slot name="header">
    <h2>我是預設 header</h2>
  </slot>
  <slot name="body">
    <h2>我是預設 body</h2>
  </slot>
  <slot name="footer">
    <h2>我是預設 footer</h2>
  </slot>
</template>

總結

  1. slot 分成預設內容插槽及具名插槽預設內容插槽可用來顯示父組件未傳入模板時的預設內容,具名插槽則需要父組件可以精確地指定要插入的內容位置。
  2. 子組件插槽可以傳遞參數子組件可以透過作用域插槽將其內部管理的參數回傳給父組件,從而使父組件能夠根據這些參數動態地改變顯示的模板內容。需要注意的是,具名插槽預設內容插槽在傳遞變數的位置上有所不同,這樣可以確保父組件中的對應模板能接收到來自特定作用域的參數。
  3. 條件插槽:子組件可以使用$slots搭配條件語句來判斷插槽是否存在,從而根據情況顯示不同的內容。
  4. 動態插槽v-slot可以綁定動態的插槽名稱,使得父組件能夠根據響應式變數動態決定要渲染哪個插槽,達到更靈活的插槽內容控制。

小試身手

slot是我學習Vue的過程中,認為最難理解的部分。一度跳過這個部分,這次也好好的把整個內容跑過一次,我並不聰明,但我始終相信這過程中所有的練習都會變成我的養分。

這次提供的練習是結合propsslot的應用(僅需調整模板上slot參數傳遞的部分):

流程說明:

  1. 父組件決定下拉式選單的變數值currentLang傳遞給子組件
  2. 子組件根據傳入的語言類型,使用slot 參數回傳要顯示的內容
  3. 父組件接收slot 參數內容後,組成對應模板傳遞給子組件顯示

👉 Vue3 Options API props 搭配 slot 結合使用練習模板
👉 Vue3 Options API props 搭配 slot 結合使用完成版本


上一篇
Vue 3 用實作帶你看過核心概念 - Day 21: 組件透傳屬性(Attributes)的應用
下一篇
Vue 3 用實作帶你看過核心概念 - Day 23:依賴注入( Provide-Inject )
系列文
Vue 3 初學者:用實作帶你看過核心概念30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言