iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 27
0
Modern Web

勇者鬥Vue龍系列 第 27

Vue.js Core 30天屠龍記(第27天): 插槽

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

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 中使用客製組件

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

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>

https://ithelp.ithome.com.tw/upload/images/20181111/20107789kggUCV4k9d.png

由於 Slots 內的 scope 是父組件的,所以子組件的 title 資料是找不到的。

Scoped Slots

有時候,你還是會需要在 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 取出來,這樣在配置的時候就可以少寫一層了。

DEMO

結語

本文講述了 Slots 的概念及使用方式,其中比較重要的有 named slot 以及 scoped slot , named slot 可以用名稱來配置不同的區塊,而 scoped slot 可以使用 slot-scope 讓我們在寫 Slots 的模板時可以使用子組件的資料。

參考資料


上一篇
Vue.js Core 30天屠龍記(第26天): 客製事件
下一篇
Vue.js Core 30天屠龍記(第28天): 動態組件
系列文
勇者鬥Vue龍32

尚未有邦友留言

立即登入留言