Vue.js: Slot
11 Oct 2017Slot 是一種用於內容分配(Content Distribution / Transclusion)的元件,適用於複雜或巢狀元件的實作上,可以想像成是空間預留的方法,在迭代過程中再把內容塞進去。
試想,遇到在元件中包含其他元件的狀況下,如下,在 <app>
中使用 <app-header>
和 <app-footer>
(可參考原始碼),要怎麼處理(1)樣版的選擇 和(2)樣版間資料的傳遞呢?
<app>
<app-header></app-header>
<app-footer></app-footer>
</app>
在這樣元件組合的狀況下,我們透過 <slot>
作為內容的插槽來解決這兩個問題。
編譯作用域(Compilation Scope)
編譯層級以所在範圍為主,意即,若在父層,則只會在父層做編譯;若在子層,則只會在子層做編譯。
範例 1:確認內容編譯的位置
「message」應該要綁定在哪一層?vm?<app-parent>
?還是<app-child>
?答案是 vm。
<div id="app">
<app-parent>
<app-child>${ message }</app-child>
</app-parent>
</div>
範例 2:試圖在父元件模版使用子元件的屬性或方法
若希望使用 isShow
控制子元件顯示與否,isShow
應該要綁在誰身上呢?
選擇 1,綁在父元件上。
<app-parent>
<app-child v-if="isShow"></app-child>
</app-parent>
選擇 2,綁在子元件上。
Vue.component('app-child', {
template: `
<div v-if="isShow">
<h1>我是子元件</h1>
</div>`,
data() {
return {
isShow: true,
};
},
});
由於父元件不應該知道子元件的資訊,因此 isShow
要綁在子元件上。
單個插槽(Single Slot)
父元件內的宿主內容會被放入子元件的 <slot>
中,只有當父元件內的宿主內容為空時,才會顯示子元件中 <slot>
內的備用內容。
如下所示,父元件內的宿主內容分別為「Hello」和「World」。
<div id="app">
<app-layout>
<app-header :id="1">Hello</app-header>
<app-footer :id="2">World</app-footer>
</app-layout>
</div>
Vue.component('app-layout', {
template: `
<div class="layout">
<h1>我是父元件的標題</h1>
<p>我是父元件的內容</p>
<slot></slot>
</div>`,
});
Vue.component('app-header', {
props: ['id'],
template: `
<div>
<h1>我是子元件 app-header 的標題 #</h1>
<slot>只有在父元件沒有要分發的內容時才會顯示。</slot>
</div>`,
});
Vue.component('app-footer', {
props: ['id'],
template: `
<div>
<h1>我是子元件 app-footer 的標題 #</h1>
<slot>只有在父元件沒有要分發的內容時才會顯示。</slot>
</div>`,
});
var vm = new Vue({
el: '#app',
});
渲染結果。
如下所示,父元件內的宿主內容為空,因此顯示子元件中 <slot>
內的備用內容。
<div id="app">
<app-layout>
<app-header :id="3"></app-header>
<app-footer :id="4"></app-footer>
</app-layout>
</div>
渲染結果。
具名插槽(Named Slots)
使用屬性 name 決定配置的內容。沒有 name 的匿名插槽將成為預設插槽,匹配不到的內容會放置在此;若沒有默認插槽,匹配不到的內容會被捨棄。
<div id="app">
<layout>
<p slot="header">這裡可能是一個頁面標題</p>
<p>主要內容的一個段落。</p>
<p>另一個主要段落。</p>
<p slot="footer">這裡有一些聯繫信息</p>
</layout>
</div>
Vue.component('layout', {
template: `
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>`,
});
var vm = new Vue({
el: '#app',
});
渲染結果。
作用域插槽(Scoped Slots)
用於資料的傳遞,使用方式很類似元件的 Props Down。
範例 1:資料傳遞範例
在子元件中,將想要傳遞的資料設定在 <slot>
的屬性 text 上,父層將用一個 <template>
標籤和 scoped 屬性註明這是給作用域插槽使用。scope="props"
中的 props 是用來保存資料的變數,如下,用來代出 text 的值。
備註:由於部落格會把花括號吃掉,因此在左右加一個點,例如「.{.{ }.}.」。
<div id="app">
<app-layout>
<app-header>
<template scope="props">
<span>.{.{ props.text }.}.</span>
</template>
</app-header>
<app-footer>
<template scope="props">
<span>.{.{ props.text }.}.</span>
</template>
</app-footer>
</app-layout>
</div>
Vue.component('app-layout', {
template: `
<div class="layout">
<h1>我是父元件的標題</h1>
<p>我是父元件的內容</p>
<slot></slot>
</div>`,
});
Vue.component('app-header', {
template: `
<div>
<h1>我是子元件 app-header 的標題</h1>
<slot text="app-header 跟大家打招呼!">
只有在父元件沒有要分發的內容時才會顯示。
</slot>
</div>`,
});
Vue.component('app-footer', {
template: `
<div>
<h1>我是子元件 app-footer 的標題</h1>
<slot text="app-footer 跟大家打招呼!">
只有在父元件沒有要分發的內容時才會顯示。
</slot>
</div>`,
});
var vm = new Vue({
el: '#app',
});
渲染結果。
範例 2:列表元件
作用域插槽較常用於 list 元件的實作上。
備註:由於部落格會把花括號吃掉,因此在左右加一個點,例如「.{.{ }.}.」。
<div id="app">
<list :items="items">
<template slot="item" scope="props">
<li>.{.{ props.text }.}.</li>
</template>
</list>
</div>
Vue.component('list', {
props: ['items'],
template: `
<ul>
<slot name="item" v-for="item in items" :text="item.text"></slot>
</ul>`,
});
var vm = new Vue({
el: '#app',
data: {
items: [
{ id: 1, text: '項目 1' },
{ id: 2, text: '項目 2' },
{ id: 3, text: '項目 3' },
],
},
});
渲染結果。