Vue躬行記(6)——內容分發

  • 2019 年 11 月 11 日
  • 筆記

  Vue提供了一種內容分發技術,可將父組件中的內容傳遞給子組件的模板,實現方式參照了Web組件規範草案。

一、插槽

  Vue內置了一個<slot>元素,能作為插槽(slot)存在,而插槽內可包含文本、HTML片段、組件等。以下面的btn組件為例,其模板中包含一個<slot>元素,在DOM中為btn組件添加了文本內容。

<btn>提交</btn>  <script>    Vue.component("btn", {      template: '<button><slot></slot></button>'    });  </script>

  渲染出的<button>元素會包含“提交”,即插槽被替換成了分發的內容,如下所示。

<button>提交</button>

  在插槽中允許添加默認的內容(即為<slot>元素附加內容,如下所示),當父組件沒有傳遞內容時,它們就會被渲染。

Vue.component("btn", {    template: '<button><slot>提交</slot></button>'  });

二、具名插槽

  具名插槽是指包含名稱的插槽,即指定了name特性的<slot>元素。當組件的模板中需要多個插槽時,就得通過名稱來加以區分。例如有一個page組件,包含三個<slot>元素,其中有兩個聲明了name特性,如下所示。

Vue.component("page", {    template: `<div>          <header><slot name="header"></slot></header>          <section><slot></slot></section>          <footer><slot name="footer"></slot></footer>      </div>`  });

  如果要向具名插槽傳遞內容,那麼可以在<template>元素上使用v-slot指令,並讓插槽名稱成為它的參數,如下所示。

<page>    <template v-slot:header>      <h1>頭部</h1>    </template>    <p>內容</p>    <template v-slot:footer>      <h1>尾部</h1>    </template>  </page>

  渲染出的DOM結構如下所示,分發的內容替換了對應的插槽。

<div>    <header>      <h1>頭部</h1>    </header>    <section>      <p>內容</p>    </section>    <footer>      <h1>尾部</h1>    </footer>  </div>

  所有沒有被包裹在帶v-slot指令的<template>元素中的內容(例如上面的<p>元素),都會傳遞給沒有名稱的插槽(即默認插槽)。

  注意,一個不帶name特性的<slot>元素,其實也有名稱,叫default。在v-slot指令中,也可以對其進行指定,如下所示。

<template v-slot:default>    <p>內容</p>  </template>

  v-slot指令不僅支援動態參數,還允許特殊的縮寫,即將參數前的v-slot:替換成#號,如下所示。

<template v-slot:[obj.header]></template>  <template #header></template>

三、作用域插槽

  在講解作用域插槽之前,需要要先了解一下編譯作用域。

1)編譯作用域

  父組件模板中的內容都是在父級作用域中編譯的,而子組件模板中的內容都是在子級作用域中編譯的,即兩級作用域中的數據無法相互訪問。下面以btn組件為例,它的模板中包含一個插槽,並在數據對象中聲明了一個txt屬性。

Vue.component("btn", {    data: function() {      return { txt:"提交" };    },    template: '<button><slot></slot></button>'  });

  在為btn組件提供插值形式的內容時,如下程式碼所示,由於當前作用域不存在txt屬性,因此會拋出錯誤。

<btn>{{txt}}</btn>

2)作用域插槽

  這是一種特殊的插槽,其內容可訪問子組件中的數據,即把模板傳給插槽而不是渲染好的內容。還是以btn組件為例,與之前不同的是,為<slot>元素自定義了一個txt特性,並為其綁定數據對象的txt屬性,如下所示。

Vue.component("btn", {    data: function() {      return { txt:"提交" };    },    template: '<button><slot :txt="txt"></slot></button>'  });

  在使用btn組件時,需要為v-slot指令傳遞一個變數,名稱可自定義(例如slots),其值是由插槽上的自定義特性所組成的對象。

<btn>    <template v-slot:default="slots">      {{slots.txt}}    </template>  </btn>

  當只提供了默認插槽時,可將v-slot指令轉移到組件上,並且可省略default名稱,如下所示。

<btn v-slot="slots">    {{slots.txt}}  </btn>

  注意,縮寫形式的默認插槽不能與具名插槽混用,因為這樣會導致作用域不明確。

<btn v-slot="slots">    {{slots.txt}}    <template v-slot:custom></template>  </btn>