iT邦幫忙

2023 iThome 鐵人賽

DAY 25
0
Vue.js

這是我的 Vue.js 筆記,不知道有沒有機會幫到你系列 第 25

我的 Vue.js 筆記(25) - slot 插槽

  • 分享至 

  • xImage
  •  

前言

不知道大家在開始使用元件的時候有沒有一個疑惑,明明元件在模版中的寫法跟一般的 HTML 標籤沒兩樣

<my-component></my-component>

但我們如果試著在裡面添加文字,卻不會有任何效果

<my-component>這句話寫在這裡不會呈現在畫面上</my-component>

原因其實是元件的內容,會是由定義元件時所寫的 template 決定,並不是由「元件標籤」的內容來定義,想想看要是元件的內容會在「使用元件」時定義,好像就失去了「拆元件」的本意了。

不過有時候如果能讓我們在使用元件的時候定義當下所需的資料,也是能讓元件的使用變得更彈性一些。

為此 Vue 提供了一個叫「插槽」的功能來達成這件事。

slot

假設我們定義了一顆按鈕元件,我們可以在元件的 template 中使用一個特殊的標籤 <slot> 讓使用元件的時候,能將資料塞在裡面。

app.component("fancy-button", {
  template: `
      <button>
        <slot></slot> <!-- 插槽 -->
      </button>`,
});

定義好 <slot> 之後,使用元件時,就可以在元件標籤內定義「插槽」要放的內容:

<fancy-button>這是一顆重要的按鈕</fancy-button>

這就可以讓我們在每次使用元件時,定義元件的「部分」資料:

<slot> 標籤的預設值

或許有些功能只有在特定情況會需要更改插槽資料,絕大多數的情況在插槽中要塞的資料都會是固定的,這種情況就很適合設定插槽的「預設值」。

設定方式很簡單,就是在元件插槽中把預設值寫好就好:

app.component("fancy-button", {
  template: `
      <button>
        <slot>預設內容</slot> <!-- 插槽 -->
      </button>`,
});

這麼一來在使用時,如果沒有寫內容的元件,就會以預設值處理了

<slot> 標籤設定名稱

如果一個元件的用途是提供一個「架構」,而他的「內容」會是在使用元件時才決定,在上面的內容已經知道可以使用 <slot> 來處理。

不過要是元件的「內容」被拆了好幾個區塊,例如「header」、「main」,一個 slot 就不夠用了。

在定義多個 slot 時,必須針對每個 slot 命名,這樣使用時的內容才有依據能夠一一對應到對的插槽中。

插槽命名的方式,只要在<slot> 標籤中添加name屬性即可。

app.component("dialog-box", {
  template: `
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot name="main"></slot>
  </main>
</div>
`,
});

實際使用時,則會使用 <template> 標籤,加上v-slot:名稱 的指令來宣告每一個區塊的內容

<dialog-box>
  <template v-slot:header>這是頭</template>
  <template v-slot:main>這是內容</template>
</dialog-box>

範例程式提供於 jsfiddle

v-slot 的簡寫

跟其他指令一樣,v-slot:也有一個 # 作為簡寫,所以上面的內容也可以寫成下面這樣:

<dialog-box>
  <template #header>這是頭</template>
  <template #main>這是內容</template>
</dialog-box>

 範例程式提供於 jsfiddle

在 slot 內容中寫 HTML 標籤

剛剛的範例中都是使用「純文字」當作 slot 的內容,不過slot 的內容也可以定義 HTML 標籤:

<dialog-box>
    <template #header>
        <h1>這是標題</h1>
  </template>
  <template #main>
      <p>這是內容</p>
  </template>
</dialog-box>

 範例程式提供於 jsfiddle

未命名的 <slot> 標籤會成為預設的 slot

如果我們在元件中使用多個 <slot> 標籤 ,有些 <slot> 標籤沒有命名的話,Vue 會把他當成預設的 slot

app.component("dialog-box", {
  template: `
    <div class="container">
    <header>
        <slot name="header"></slot>
    </header>
    <main>
        <slot></slot>  <!-- 他會被當成預設 slot-->
    </main>
    </div>
    `,
});

如果要在模板中塞資料到這個 slot 中,可以用 default 的字樣來宣告:

<dialog-box>
  <template #header>這是頭</template>
  <template #default>這是內容</template>
</dialog-box>

不過還記得一開始沒對 <slot> 標籤命名的時候,我們是怎麼塞資料到 slot 的嗎?
對,是直接把資料寫在元件標籤內:

<fancy-button>這是一顆重要的按鈕</fancy-button>

換言之,如果我們直接在元件標籤寫資料,內容會直接塞到「預設 slot」裡面:

<dialog-box>
  <template #header>
    <h1>這是標題</h1>
  </template>

  <p>這是內容1</p>
  <p>這是內容2</p>
  <p>這是內容3</p>
  <p>這是內容4</p>
</dialog-box>

 範例程式提供於 jsfiddle

在元件中先定義好資料,使用時再決定結構

一般的元件操作方式,通常會是「元件定義結構」,從外部使用元件時,再把資料塞到元件中,讓元件渲染出資料。

不過如果透過插槽,Vue 有辦法做到在元件中定義資料,使用元件時再決定結構,做法是在元件的 <slot> 標籤定義「屬性」,就像 props 那樣。

這裡定義一個 text 的屬性,並且把元件的 msg 變數綁上去。

app.component("my-component", {
  template: `
    <div class="container">
        <slot :text="msg"></slot>
    </div>
`,
  setup() {
    const msg = Vue.ref("Hello World");
    return {
      msg,
    };
  },
});

在模版中,在 v-slot 定義一個名稱,即可以拿他取得 text 的資料

<my-component v-slot="slotProps">{{slotProps.text}}</my-component>

這個名稱不一定要叫 slotProps ,可以依當下需求定義。

 範例程式提供於 jsfiddle


上一篇
我的 Vue.js 筆記(24) - 動態元件 is 與 keep-alive
下一篇
我的 Vue.js 筆記(26) - 使用 Vite 開發 Vue
系列文
這是我的 Vue.js 筆記,不知道有沒有機會幫到你30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言