Vue.js: Slot

Vue

Slot 是一種用於內容分配(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',
});

Single Slot

渲染結果。

Single Slot

如下所示,父元件內的宿主內容為空,因此顯示子元件中 <slot> 內的備用內容。

<div id="app">
  <app-layout>
    <app-header :id="3"></app-header>
    <app-footer :id="4"></app-footer>
  </app-layout>
</div>

Single Slot

渲染結果。

Single Slot

完整程式碼

具名插槽(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',
});

Named Slots

渲染結果。

Named Slots

完整程式碼

作用域插槽(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',
});

Scoped Slots

渲染結果。

Scoped Slots

完整程式碼

範例 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' },
    ],
  },
});

Scoped Slots

渲染結果。

Scoped Slots

完整程式碼

推薦閱讀

以上參考-Content Distribution with Slots — Vue.js


vue.js vue.js components