傳遞顯示型 Data

關於顯示於 Component 的 data,除了使用 prop 傳遞外,也可使用 slot 傳遞。

Version


Vue 2.5.17
Vue CLI 3.0.3

Slot Content


1
2
3
<navigation-link url="/profile">
Your Profile
</navigation-link>

在自己寫了 component 間夾了 data。

1
2
3
<a v-bind:href="url"class="nav-link">
<slot></slot>
</a>

在 component 的 HTML template 中使用 <slot></slot>,則 component 間夾的 data 將取代 <slot></slot>

若 HTML template 內沒有 <slot></slot>,則 data 將不會顯示

最後 HTML 為:

1
2
3
<a v-bind:href="url"class="nav-link">
Your Profile
</a>

<slot></slot> 可視為 placeholder,專門顯示 component tag 間的 data,如此就不必什麼 data 都靠 prop 傳遞。

Slot 可是為傳遞 data 的另外一種方式,尤其是 顯示型 的 data

1
2
3
4
5
<navigation-link url="/profile">
<!-- Add a Font Awesome icon -->
<span class="fa fa-user"></span>
Your Profile
</navigation-link>

Component tag 間不單只能放 data,也可以放其他 HTML。

1
2
3
4
5
<navigation-link url="/profile">
<!-- Use a component to add an icon -->
<font-awesome-icon name="user"></font-awesome-icon>
Your Profile
</navigation-link>

Component tag 間也可以放其他 component。

Named Slot


Vue 也允許在一個 component 間有多個 slot,此時必須使用 Named Slot

1
2
3
4
5
6
7
8
9
10
11
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>

我們希望 base-layout component 的 headermainfooter 三部分都使用 slot,由 user 提供 data。

一個 component 只能有一個 default slot,其他都必須是 named slot

所以在 headerfooter 使用了 named slot,在 <slot></slot> 多了 name,設定 slot 的名稱為 headerfooter

main 則使用 default slot,不特別為 slot 取名稱。

1
2
3
4
5
6
7
8
9
10
11
12
<base-layout>
<template slot="header">
<h1>Here might be a page title</h1>
</template>

<p>A paragraph for the main content.</p>
<p>And another one.</p>

<template slot="footer">
<p>Here's some contact info</p>
</template>
</base-layout>

使用 base-layout component 時,由於有多個 slot,要搭配 template tag 將 data 與 HTML 包起來,並使用 slot attribute 指定 slot 名稱。

沒使用 template tag 部分,則為 default slot。

1
2
3
4
5
6
7
8
<base-layout>
<h1 slot="header">Here might be a page title</h1>

<p>A paragraph for the main content.</p>
<p>And another one.</p>

<p slot="footer">Here's some contact info</p>
</base-layout>

也可以直接對 HTML 加上 slot attribute。

最後 HTML 為:

1
2
3
4
5
6
7
8
9
10
11
12
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>

3 個 slot 完全被取代。

Default Slot Content


Slot 雖然目的就是由 user 提供 data 顯示,但有時候會想在 component 內提供預設顯示 data。

1
2
3
<button type="submit">
<slot>Submit</slot>
</button>

submit-button component 預設顯示 Submit,但也允許 user 提供其他 data 顯示,如 SaveUpload,就可在 <slot></slot> 之間夾預設顯示 data。

Scoped Slot


若想由 user 自訂 slot,但實際 data 卻是在 component 內,此時就要使用 Scoped Slot

1
2
3
4
5
<ul>
<li v-for="(todo, index) in todos" :key="index">
{{ todo.title }}
</li>
</ul>

todo-list component 雖然已經提供 todo.title 的顯示,但為了讓 component 更有彈性,想提供 slot 由 user 能夠提供不同顯示方式。

1
2
3
4
5
6
7
<ul>
<li v-for="(todo, index) in todos" :key="index">
<slot :todo="todo">
{{ todo.title }}
</slot>
</li>
</ul>

todo 以 prop 方式傳進 slot,此為預設顯示方式。

1
2
3
4
5
6
<todo-list :todos="todos">
<template slot-scope="slotProps">
<span v-if="slotProps.todo.completed"></span>
{{ slotProps.todo.text }}
</template>
</todo-list>

User 提供了不同的顯示方式取代 slot。

<template> 加上 slot-scope attribute 指定 scope 名稱,然後使用 scope 名稱存取 component 內的 data。

不一定得使用 <template> tag,任何 HTML tag 加上 slot-scope 皆可

1
2
3
4
5
6
<todo-list :todos="todos">
<template slot-scope="{ todo }">
<span v-if="todo.completed"></span>
{{ todo.text }}
</template>
</todo-list>

若搭配 ECMAScript 2015 的 Object Destructuring,則有更好的寫法。

因為 slotProps 本質就是有 todo property 的 object。

可使用 { todo }todo 取出為變數直接使用,則不必在定義 slotProps 這種中介變數。

Conclusion


  • 有了 Slot,顯示型的 data 就不必再使用 prop,可直接使用 slot 傳遞
  • 若 component 提供多個 slot,則要使用 Named Slot
  • Slot 也可提供預設顯示 data
  • 若想提供 user 自訂 slot,卻要讀取 component 內的資料,則要使用 Scoped Slot;若搭配 ECMAScript 2015 的 Object Destructuring 寫法,則非常精簡

Sample Code


完整的範例可以在我的 GitHub 上找到

Reference


Vue, Slots

2018-10-23