Vue.js 實作了 Web Component 的 Slots , Slots 可以將父組件所設定的內容分配至子組件中。
在子組件中設定 <slot>
,可以將父組件中的元素內容配置在這個 <slot>
中。
如下例的 base-button
:
Vue.component('base-button', {
template: `
<button>
<slot></slot>
</button>
`
});
在 <button>
中設定 <slot>
。
接著在父組件內加上要配置在 <slot>
的內容:
<base-button>Hello Button</base-button>
如此一來 <slot>
會被置換成 Hello Button ,所以會渲染為下面這樣:
<button>Hello Button</button>
上面就是最基本的 Slots 應用。
slots 中不只能設定字串,也能用 HTML 內容作設定。
例如將剛才的 base-button
改成下面這樣配置:
<base-button><span style="color: red;">Hi</span> Button</base-button>
結果如下:
<button><span style="color: red;">Hi</span> Button</button>
slots 中也可以加上客製組件。
現在我們加上一個 base-title
的組件:
Vue.component('base-title', {
props: ['title'],
template: `
<div>
<h2>{{title}}</h2>
<slot></slot>
</div>
`
});
接著在父組件的 slots
中設置 base-button
:
<base-title title="Title">
<base-button>Big Button</base-button>
</base-title>
最後的結果如下:
<div><h2>Title</h2> <button>Big Button</button></div>
如果在子組件中沒有設定
<slot>
標籤,在父組件內的內容將會被忽略。
有時候會需要嵌入數個不同位置的內容,常發生在 Layout 組件上,這時可以使用 <slot>
的 name
屬性來定義不同位置的 Slots 內容。
如下例的 base-layout
組件:
Vue.component('base-layout', {
template: `
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
`
});
在模板上設定三個 <slot>
:
<slot name="header">
: 在 <header>
標籤中。<slot>
: 在 <main>
標籤中。<slot name="footer">
: 在 <footer>
標籤中。接著在父組件中用 slot
屬性設定對應名稱的內容:
<base-layout>
<template slot='header'>Header</template>
Content
<template slot='footer'>Footer</template>
</base-layout>
如此一來,模板會依照 slot
的位置渲染各個不同名稱的 <slot>
,結果如下:
<div class="container">
<header>Header</header>
<main>Content</main>
<footer>Footer</footer>
</div>
這裡有三點要注意:
Content
字串因為沒有設定 slot
屬性,所以會被當作沒有名字的 <slot>
內容。<template>
會是一個不會出現在頁面上的標籤,當 Slots 中的內容有多個元素時,可以使用 <template>
設定 slot
。<template>
設定 <slot>
,但 slot
也可以設置在一般的元素(像是 <p>
)或是客製組件上(像是 base-button
)。有時插槽的內容是比較固定的,只有一些比較特別的時候會需要自己做設置,如果每次都一定要設置內容的話會變得很繁瑣,因此 Slots 提供了預設內容的功能。
如下例所示:
Vue.component('base-button', {
template: `
<button>
<slot>BASE</slot>
</button>
`
});
我們在 base-button
中的 <slot>
加上內容 BASE
,這樣當我們在父組件不設置任何的內容時就會使用 BASE
當作內容渲染:
<base-button></base-button>
這樣渲染的結果會是以 BASE
為名稱的按鈕:
<button>BASE</button>
Scope 會決定目前內容可以使用的變數有哪些,在 slots 中,雖然它是渲染在子組件內,但因為它是在父組件中的模板定義,所以它的 scope 會是父組件的模板。
我們用 base-title
當作範例,它的模板如下:
<div>
<h2>{{title}}</h2>
<slot></slot>
</div>
現在設定父組件:
var vm = new Vue({
el: '#app',
data: {
content: 'parent contecnt'
}
});
<base-title title="child content">Your content is {{content}}</base-title>
content
是父組件中的資料屬性,可以設定在 Slots 中。
接著我們看如果是要使用 title
這個子組件的資料會發生什麼事:
<base-title title="child content">Your title is {{title}}</base-title>
由於 Slots 內的 scope 是父組件的,所以子組件的 title
資料是找不到的。
有時候,你還是會需要在 Slots 中設定子組件的資料,因此 Vue.js 允許你使用 slot-scope
屬性設定子組件的 scope 物件,讓父組件在設定內容時也可以使用子組件的資料。
以剛剛的 base-title
為例:
<div>
<h2>{{title}}</h2>
<slot :child-title="title"></slot>
</div>
在 <slot>
上綁定 title
設為 child-title
,這麼做是因為只有綁定在 <slot>
的屬性才能被 scope 設定所綁定。
可以同時綁定多個屬性,像是
<slot :child-title="title" :child-data="childData"></slot>
,也可以綁定物件。
接著看父組件:
<base-title title="child content">
<template slot-scope="child">
<div>content: {{content}}</div>
<div>title: {{child.childTitle}}</div>
</template>
</base-title>
在 <template>
上設定的 slot-scope
是要設置子組件的 scope 名字,在內容中就可以像一般資料一樣使用 {{}}
綁定。
slot-scope
slot-scope
會把每個綁定的屬性當作物件的屬性,因此我們可以使用 destructuring 來取得想要的屬性。
如下例所示:
<base-title title="child content">
<template slot-scope="{ childTitle }">
<div>content: {{content}}</div>
<div>title: {{childTitle}}</div>
</template>
</base-title>
我們只把需要的 childTile
取出來,這樣在配置的時候就可以少寫一層了。
本文講述了 Slots 的概念及使用方式,其中比較重要的有 named slot 以及 scoped slot , named slot 可以用名稱來配置不同的區塊,而 scoped slot 可以使用 slot-scope
讓我們在寫 Slots 的模板時可以使用子組件的資料。