Vue Component 之 Slot
關於顯示於 Component 的 data,除了使用 prop 傳遞外,也可使用 slot 傳遞。
Version
Vue 2.5.17
Vue CLI 3.0.3
Slot Content
<navigation-link url="/profile"> Your Profile </navigation-link>
在自己寫了 component 間夾了 data。
<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 為:
<a v-bind:href="url"class="nav-link"> Your Profile </a>
<slot></slot>
可視為 placeholder,專門顯示 component tag 間的 data,如此就不必什麼 data 都靠 prop 傳遞。
Slot 可是為傳遞 data 的另外一種方式,尤其是顯示型
的 data
<navigation-link url="/profile"> <!-- Add a Font Awesome icon --> <span class="fa fa-user"></span> Your Profile </navigation-link>
Component tag 間不單只能放 data,也可以放其他 HTML。
<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 。
<div class="container"> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div>
我們希望base-layout
component 的header
、main
與footer
三部分都使用 slot,由 user 提供 data。
一個 component 只能有一個default slot ,其他都必須是named slot
所以在header
與footer
使用了 named slot,在<slot></slot>
多了name
,設定 slot 的名稱為header
與footer
。
main
則使用 default slot,不特別為 slot 取名稱。
<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。
<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 為:
<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。
<button type="submit"> <slot>Submit</slot> </button>
submit-button
component 預設顯示Submit
,但也允許 user 提供其他 data 顯示,如Save
與Upload
,就可在<slot>
與</slot>
之間夾預設顯示 data。
Scoped Slot
若想由 user 自訂 slot,但實際 data 卻是在 component 內,此時就要使用Scoped Slot 。
<ul> <li v-for="(todo, index) in todos" :key="index"> {{ todo.title }} </li> </ul>
todo-list
component雖然已經提供todo.title
的顯示,但為了讓 component 更有彈性,想提供 slot 由 user 能夠提供不同顯示方式。
<ul> <li v-for="(todo, index) in todos" :key="index"> <slot :todo="todo"> {{ todo.title }} </slot> </li> </ul>
將todo
以 prop 方式傳進slot,此為預設顯示方式。
<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
皆可
<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 寫法,則非常精簡