我們知道在HTML中標籤可以分爲閉合標籤和空標籤,其中大多數都是閉合標籤,只有少數空標籤,比如:<input/>
、`<img/>
、<base/>
、<hr/>
等。在Vue中定義的組件也可以採用兩種方式來書寫。
在一個組件的<template>
中假如顯示一個標題<h1>Hello World!</h1>
,當我們在調用時,不管是採用<組件名/>
或者<組件名></組件名>
都會顯示 “hello world!“,由於在閉合標籤中可以包含子節點和文本,因此我們可以這樣來使用<組件名>我是內容</組件名>
,但是運行的結果,確什麼也沒有改變。
如果想要在組件傳入的內容正確投射,就需要一套機制來處理,在Vue中自然就是slot(插槽)機制,在React中可以通過this.props.children
來獲取,而在Angular中可以通過<ng-content></ng-content>
來放置轉入的內容。
React 的處理方式
在React中傳入this.props.children
的值有三種可能:
- 沒有子節點,結果爲
undefined
- 子節點,結果爲
object
- 多個子節點,結果爲
array
如果要對傳入的數據進行處理,React還提供了一個工具方法React.Children
來處理this.props.children
。總的來說React把一切處理完全交給開發人員來,完全透明。
React的這種處理方式相對來說也更加靈活,對於Vue的作用域插槽機制也能很簡單的實現。
Angular 的處理方式
相對於React的完全透明模式,Angular則在傳入內容時通過屬性、類(class)和標籤三種模式來區分和限定內容的作用域。在組件內容使用<ng-content select="xx"></ng-content>
來作爲佔位,具休使用方式如下:
- 傳屬性及屬性值
<div card-body>屬性</div>
<ng-content select="[card-body]"></ng-content>
<div card-type="body">屬性值</div>
<ng-content select="[card-type=body]"></ng-content>
<div card body>多個屬性值組合</div>
<ng-content select="[card][body]"></ng-content>
- 傳類(class)
<div class=".card-body">類</div>
<ng-content select=".card-body"></ng-content>
<div class="card body">多個類組合</div>
<ng-content select=".card.body"></ng-content>
- 傳標籤
<card-body></card-body>
<ng-content select="card-body"></ng-content>
- 多個插槽
<ng-content select="header"></ng-content>
<div class="body">balabala...</div>
<ng-content select="footer"></ng-content>
從上面的使用方式來看Angular的插槽功能還是很強大的。
Vue 的處理方式
迴歸到本文的主題,在Vue 2.6.0過後引入了新語法機制將以前版本的slot
和scope-slot
使用一個屬性來表示。具體緣由可查看其RFC。
在新的語法中v-slot
只允許使用在 組件 和 <template></template>
標籤中,並且在只能<template>
套組件和子<template>
。
- 默認插槽
在組件中直接放一個<slot></slot>
標籤就可以接收來自組件中的內容,如果<slot>
不爲空,那麼內容將作爲默認值顯示。
在調用時就可以使用v-slot:default
在組件上或者其子節點<template>
上。
- 具名插槽及縮寫
具名插槽就是把分佈在頁面中不同位置的<slot>
分配一個名字來標識,以區分其功能。在使用時只需要給<slot>
添加一個name
屬性即可,例如:<slot name="footer"></slot>
。在使用時把默認插槽的default
換成相應的名字即可,即:<template v-slot:footer></template>
。
在使用過程沒有必要每次都重複寫v-slot:
,同v-on
和v-bind
一樣,v-slot
也有其縮寫形式,即把參數之前的所有內容 (v-slot:
) 替換爲字符 #
,即<template #footer></footer>
- 作用域插槽
作用域插槽的機制就是被調用的組件把組件內部的狀態通過屬性暴露給當前上下文。簡單點說就是它只提供數據,至於當前數據怎麼展示它不關心,類似於React的Render Props機制。
由於Vue官方文檔寫得很清楚明白這裏直接上知識點:
- 解構插槽Prop
<current-user v-slot="{ user }">
{{ user.firstName }}
</current-user>
- 解構時提供別名和提供初始值
<current-user v-slot="{ user: person }">
{{ person.firstName }}
</current-user>
<current-user v-slot="{ user = { firstName: 'Guest' } }">
{{ user.firstName }}
</current-user>
官方提到在只有默認插槽時可以使用插槽的縮寫語法,將v-slot:default="slotProps"
寫成v-slot="slotProps"
,但是在使用時官方也說了不能和具名插槽混用,因爲它會導致作用域不明確。
下面通過一個示例來實現React中的Render Props機制。
<template>
<div style="height:100%" @mousemove="handleMouseMove">
<p>The current mouse position is {{ x }}, {{ y }}</p>
<slot :position="position"/>
</div>
</template>
<script>
export default {
name: 'Mouse',
data() {
return {
x: 0,
y: 0
}
},
computed: {
position: function() {
return {
x: this.x,
y: this.y
}
}
},
methods: {
handleMouseMove(event) {
this.x = event.clientX
this.y = event.clientY
}
}
}
</script>
然後在其它組件中調用
<mouse>
<template v-slot:default="{position}">
<img src="http://iph.href.lu/64x64?text=圖片跟隨鼠標" :style="{position: 'absolute', left: position.x + 'px', top: position.y + 'px'}"/>
</template>
</mouse>
- 動態插槽
動態插槽歡迎通過編程動態控制當前組件的插槽名,使用方式如下:
<template v-slot:[dynamicSlotName]>...</template>
- 多層插槽的嵌套
<foo v-slot="foo">
<bar v-slot="bar">
<baz v-slot="baz">
{{ foo }} {{ bar }} {{ baz }}
</baz>
</bar>
</foo>
總結
本文只是簡單對Vue的Slot的知識點做一個簡單的筆記,更多的知識點官方文檔比較靠譜,文中並沒有提到在JSX語法下的插槽使用方式,如果想要了解請查看我的另外一篇文章Vue中jsx不完全應用指南關於插槽部分的內容。