Vue 插槽(slot)使用(通俗易懂)

由於在2.6.0中,具名插槽做用域插槽 引入了一個新的統一的語法 (即v-slot 指令)。它取代了 slotslot-scope,而且如今網上都說的是一些老版本的內容,官方文檔不太容易理解,因此就整理了一篇有關於插槽(slot)使用的文章css


Slot 通俗的理解就是「佔坑」,在組件模板中佔好了位置,當使用該組件標籤時候,組件標籤裏面的內容就會自動填坑(替換組件模板中slot位置)
而且能夠做爲承載分發內容的出口html

內容插槽

定義兩個組件 home.vuetest.vue
而後在home.vue組件中引用test.vue組件vue

插槽內能夠包含普通文本bootstrap

//home.vue
<test>
     Hello Word
</test>
複製代碼
//test.vue
<a href="#">
	 <slot></slot>
</a>
複製代碼

當組件渲染的時候,<slot></slot>會被替換爲Hello Wordbash

插槽內也能夠包含任何模板代碼,包括HTML函數

在你的index.html引入Font Awesome圖標的樣式就直接能夠用那裏面的圖標了
<link href="//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">佈局

//home.vue
<test>
    <!-- 添加一個 Font Awesome 圖標 -->
    <span class="fa fa-user"></span>
    Hello Word
</test>
複製代碼

插槽內添加其餘組件ui

//home.vue
<test>
    <!-- 添加一個圖標的組件 -->
    <font-awesome-icon></font-awesome-icon>
    Hello Word
</test>
複製代碼

若是<test>中沒有包含一個<slot>元素,則該組件起始標籤和結束標籤之間的任何內容都會被拋棄。spa

在插槽中使用數據設計

插槽跟模板其餘地方同樣均可以訪問相同的實例屬性(也就是相同的"做用域"),而不能訪問<test>的做用域

//home.vue
<test>
	//插槽能夠獲取到home組件裏的內容
	Hello {{enhavo}}
</test>

data(){
	return{
		enhavo:'word'
	}
}
複製代碼
//home.vue
//這裏是獲取不到name的,由於這個值是傳給<test>的
<test name='you'>
    Hello {{name}}
</test>
複製代碼

規則:
父級模板裏的全部內容都是在父級做用域中編譯的;子模板裏的全部內容都是在子做用域中編譯的。

後備內容(默認內容)插槽

有時候咱們須要給插槽設置一個具體的默認內容,當別的組件沒有給你內容的時候,那麼默認的內容就會被渲染

//test.vue
//在slot插槽裏設置默認內容 Submit
<button>
  <slot>Submit</slot>
</button>
複製代碼

home.vue裏直接使用test.vue以下:

//home.vue
<test></test>
複製代碼

那麼最後設置的默認內容 Submit 將會被渲染

<button>
   Submit
</button>
複製代碼

假如咱們提供內容呢?

//home.vue
<test>按鈕</test>
複製代碼

那麼這個提供的內容將會替代默認的內容被渲染出來

<button>
   按鈕
</button>
複製代碼

具名插槽

有時候咱們一個組件裏須要多個插槽

那麼怎麼辦呢? 對於這樣的狀況,<slot>元素有一個特殊的特性:name ,這個特性能夠用來定義額外的插槽

<div>
  <header>
    <!-- 咱們但願把頁頭放這裏 -->
  </header>
  
  <main>
    <!-- 咱們但願把主要內容放這裏 -->
  </main>
  
  <footer>
    <!-- 咱們但願把頁腳放這裏 -->
  </footer>
</div>
複製代碼

這時候,咱們就可使用name屬性

<div>
  <header>
    <slot name="header"></slot>
  </header>
  
  <main>
    <slot></slot>
  </main>
  
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>
複製代碼

若是一個<slot>不帶name屬性的話,那麼它的name默認爲default
在向具名插槽提供內容的時候,咱們能夠在<template>元素上使用v-slot指令,並以參數的形式提供其名稱

<div>
   <template v-slot:header>
    <h1>Here might be a page title</h1>
   </template>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template v-slot:footer>
    <p>Here some contact info</p>
  </template>
</div>
複製代碼

如今 <template> 元素中的全部內容都將會被傳入相應的插槽。任何沒有被包裹在帶有 v-slot<template> 中的內容都會被視爲默認插槽的內容。

若是你但願更明確一點的話,那就把主體內容那個插槽裏設置name="default",而後把上面的內容包裹起來

<template v-slot:default>
	<p>A paragraph for the main content.</p>
	<p>And another one.</p>
</template>
複製代碼

注:v-slot只能添加在一個<template>上,(只有一種例外狀況,下面會說)

做用域插槽

上面已經說了,插槽跟模板其餘地方同樣均可以訪問相同的實例屬性(也就是相同的"做用域"),而不能訪問<test>的做用域

那若是想訪問<test>做用域該怎麼辦呢?
咱們把須要傳遞的內容綁到 <slot> 上,而後在父組件中用v-slot設置一個值來定義咱們提供插槽的名字:

//test.vue
<div>
	<!-- 設置默認值:{{user.lastName}}獲取 Jun -->
	<!-- 若是home.vue中給這個插槽值的話,則不顯示 Jun -->
	<!-- 設置一個 usertext 而後把user綁到設置的 usertext 上 -->
	<slot v-bind:usertext="user">{{user.lastName}}</slot>
</div>

//定義內容
data(){
  return{
	user:{
	  firstName:"Fan",
	  lastName:"Jun"
	}
  }
}
複製代碼

而後在home.vue中接收傳過來的值:

//home.vue
<div>
  <test v-slot:default="slotProps">
    {{slotProps.usertext.firstName}}
  </test>
</div>
複製代碼

這樣就能夠得到test.vue組件傳過來的值了

綁定在 <slot> 元素上的特性被稱爲插槽 prop。在父組件中,咱們能夠用 v-slot 設置一個值來定義咱們提供的插槽 prop 的名字,而後直接使用就行了

獨佔默認插槽的縮寫語法

在上述狀況下,當被提供的內容只有默認插槽時,組件的標籤才能夠被看成插槽的模板來使用。這樣咱們就能夠把 v-slot 直接用在組件上

這樣寫法還能夠更簡單,由於不帶參數的v-slot就被假定爲默認插槽,因此上面的代碼還能夠簡化:

<div>
  <!-- 能夠把 :default 去掉,僅限於默認插槽 -->
  <test v-slot="slotProps">
    {{slotProps.usertext.firstName}}
  </test>
</div>
複製代碼

注: 默認插槽 的縮寫語法不能和 具名插槽 混用,由於它會致使做用域不明確

<div>
  <!-- 能夠把 :default 去掉,僅限於默認插槽 -->
  <test v-slot="slotProps">
    {{slotProps.usertext.firstName}}
    <!-- 無效,會警告 -->
    <template v-slot:other="otherSlotProps">
      slotProps is NOT available here
    </template>
  </test>
</div>
複製代碼

只要出現多個插槽,始終要爲全部的插槽使用完整的基於<template>的語法:

<test>
  <template v-slot:default="slotProps">
    {{ slotProps.user.firstName }}
  </template>

  <template v-slot:other="otherSlotProps">
    ...
  </template>
</test>
複製代碼

解構插槽Prop

由於 做用域插槽 的內部工做原理是將你的插槽內容包括在一個傳入單個參數的函數裏
這意味着 v-slot 的值實際上能夠是任何可以做爲函數定義中的參數的 JS 表達式

因此原本是這樣寫的:

<div>
  <test v-slot="slotProps">
    {{slotProps.usertext.firstName}}
  </test>
</div>
複製代碼

還能夠這樣寫:

<div>
  <test v-slot={usertext}>
    {{usertext.firstName}}
  </test>
</div>
複製代碼

這樣可使模板更簡潔,尤爲是在該插槽提供了多個 prop 的時候。它一樣開啓了 prop 重命名等其它可能,

例如能夠將 usertext 重命名爲 person:

<div>
  <test v-slot={usertext:person}>
    {{person.firstName}}
  </test>
</div>
複製代碼

甚至能夠定義 後備內容(默認內容),用於插槽沒有值時可使用默認內容的情形:

<div>
  <test v-slot="{usertext={firstName:'Yang'}}">
    {{usertext.firstName}}
  </test>
</div>
複製代碼

動態插槽名(2.6.0新增)

動態指令參數(須要本身瞭解)也能夠用在v-slot上,來定義動態的插槽名:

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>
</base-layout>
複製代碼

具名插槽的縮寫(2.6.0新增)

v-onv-bind 同樣,v-slot 也有縮寫,即把參數以前的全部內容 (v-slot:) 替換爲字符 #。例如 v-slot:header 能夠被重寫爲 #header

原來是這樣寫的:

<div>
   <template v-slot:header>
    <h1>Here might be a page title</h1>
   </template>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template v-slot:footer>
    <p>Here some contact info</p>
  </template>
</div>  
複製代碼

如今能夠這樣寫:

<div>
   <template #header>
    <h1>Here might be a page title</h1>
   </template>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template #footer>
    <p>Here some contact info</p>
  </template>
</div>
複製代碼

注:該指令和其餘指令同樣,只在其有參數的時候纔可用

下面的書寫形式是錯誤的:

<test #="{ usertext }">
  {{ usertext.firstName }}
</test>
複製代碼

若是但願使用縮寫的話,必須始終以明確插槽名取而代之

<test #default="{ usertext }">
  {{ usertext.firstName }}
</test>
複製代碼

其餘示例

插槽 prop 容許咱們將插槽轉換爲可複用的模板,這些模板能夠基於輸入的 prop 渲染出不一樣的內容。 這在設計封裝數據邏輯同時容許父級組件自定義部分佈局的可複用組件時是最有用的。

例如,咱們要實現一個 <todo-list> 組件,它是一個列表且包含佈局和過濾邏輯:

<ul>
  <li
    v-for="todo in filteredTodos"
    v-bind:key="todo.id"
  >
    {{ todo.text }}
  </li>
</ul>
複製代碼

咱們能夠將每一個 todo 做爲父級組件的插槽,以此經過父級組件對其進行控制,而後將 todo 做爲一個插槽 prop 進行綁定:

<ul>
  <li
    v-for="todo in filteredTodos"
    v-bind:key="todo.id"
  >
    <!--
    咱們爲每一個 todo 準備了一個插槽,
    將 `todo` 對象做爲一個插槽的 prop 傳入。
    -->
    <slot name="todo" v-bind:todo="todo">
      <!-- 後備內容 -->
      {{ todo.text }}
    </slot>
  </li>
</ul>
複製代碼

如今當咱們使用 <todo-list> 組件的時候,咱們能夠選擇爲 todo 定義一個不同的 <template> 做爲替代方案,而且能夠從子組件獲取數據:

<todo-list v-bind:todos="todos">
  <template v-slot:todo="{ todo }">
    <span v-if="todo.isComplete">✓</span>
    {{ todo.text }}
  </template>
</todo-list>
複製代碼

至於那些廢棄了的 slotslot-scope 特性,這裏就不在闡述了,若是有興趣瞭解的話,請參考官方文檔

拜拜

@_@

相關文章
相關標籤/搜索