大家好我是林三心,Vue 實現了一套內容分發的 API,將<slot>
元素作為承載分發內容的出口,這是Vue文件上的說明。具體來說,slot
就是可以讓你在元件內新增內容的‘空間’,你真的知道插槽Slot是怎麼“插”的嗎?我希望你們能像我一樣單純,老老實實地看這篇文章。
Vue插槽slot的基本使用
單個插槽 | 匿名插槽
//子元件 : (假設名為:child)
<template>
<div class= 'child'>
</div>
</template>
//父元件:(引用子元件 child)
<template>
<div class= 'app'>
<child>
林三心
</child>
</div>
</template>
我們知道,如果直接在父元件中的<child></child> 中新增內容“林三心”,“林三心”文字是不會在頁面上渲染的。那麼我們如何使新增的內容能夠顯示呢?在子元件內新增slot 即可。
//子元件 : (假設名為:child)
<template>
<div class= 'child'>
<slot></slot>
</div>
</template>
編譯作用域 (父元件在子元件<slot></slot>
處插入 data)
上面我們瞭解了,slot 其實就是能夠讓我們在父元件中新增內容到子元件的‘空間’。我們可以新增父元件內任意的data值,比如這樣:
//父元件:(引用子元件 child)
<template>
<div class= 'app'>
<child> {{ parent }}</child>
</div>
</template>
new Vue({
el:'.app',
data:{
parent:'父元件'
}
})
使用資料的語法完全沒有變,但是,我們能否直接使用子元件內的資料呢?顯然不行!!
// 子元件 : (假設名為:child)
<template>
<div class= 'child'>
<slot></slot>
</div>
</template>
new Vue({
el:'child',
data:{
child:'子元件'
}
})
// 父元件:(引用子元件 child)
<template>
<div class= 'app'>
<child> {{ child }}</child>
</div>
</template>
直接傳入子元件內的資料是不可以的。因為:父級模板裡的所有內容都是在父級作用域中編譯的;子模板裡的所有內容都是在子作用域中編譯的。
後備內容 (子元件<slot></slot>
設定預設值)
所謂的後背內容,其實就是slot的預設值,有時我沒有在父元件內新增內容,那麼 slot就會顯示預設值,如:
//子元件 : (假設名為:child)
<template>
<div class='child'>
<slot>這就是預設值</slot>
</div>
</template>
具名插槽 (子元件多個<slot></slot>
對應插入內容)
有時候,也許子元件內的slot不止一個,那麼我們如何在父元件中,精確的在想要的位置,插入對應的內容呢? 給插槽命一個名即可,即新增name屬性。
//子元件 : (假設名為:child)
<template>
<div class= 'child'>
<slot name='one'> 這就是預設值1</slot>
<slot name='two'> 這就是預設值2 </slot>
<slot name='three'> 這就是預設值3 </slot>
</div>
</template>
父元件通過,或slot="name"(舊語法)
,v-slot:name
或#name(新語法)
的方式新增內容:
//父元件:(引用子元件 child)
<template>
<div class= 'app'>
<child>
<template v-slot:"one"> 這是插入到one插槽的內容 </template>
<template v-slot:"two"> 這是插入到two插槽的內容 </template>
<template v-slot:"three"> 這是插入到three插槽的內容 </template>
</child>
</div>
</template>
作用域插槽 (父元件在子元件<slot></slot>
處使用子元件 data)
通過slot
我們可以在父元件為子元件新增內容,通過給slot
命名的方式,我們可以新增不止一個位置的內容。但是我們新增的資料都是父元件內的。上面我們說過不能直接使用子元件內的資料,但是我們是否有其他的方法,讓我們能夠使用子元件的資料呢? 其實我們也可以使用slot-scope
的方式:
//子元件 : (假設名為:child)
<template>
<div class= 'child'>
<slot name= 'one' :value1='child1'> 這就是預設值1</slot> //繫結child1的資料
<slot :value2='child2'> 這就是預設值2 </slot> //繫結child2的資料,這裡我沒有命名slot
</div>
</template>
new Vue({
el:'child',
data:{
child1:'資料1',
child2:'資料2'
}
})
//父元件:(引用子元件 child)
<template>
<div class='app'>
<child>
<template v-slot:one='slotone'>
{{ slotone.value1 }} // 通過v-slot的語法 將子元件的value1值賦值給slotone
</template>
<template v-slot:default='slotde'>
{{ slotde.value2 }} // 同上,由於子元件沒有給slot命名,預設值就為default
</template>
</child>
</div>
</template>
Slot插槽是怎麼“插”的(通俗版)
普通插槽
//子元件 : (假設名為:child)
<template>
<div class='child'>
我在子元件裡面
<slot></slot>
<slot name="one"></slot>
</div>
</template>
//父元件:(引用子元件 child)
<template>
<div class= 'app'>
<child>
這是插入到預設插槽的內容 {{parent}}
<template v-slot:"one"> 這是插入到one插槽的內容 {{parent}}</template>
</child>
</div>
</template>
new Vue({
el:'.app',
data:{
parent:'父元件的值'
}
})
父元件先解析,把
child
當做子元素處理,把 插槽當做child
的子元素處理,並且在父元件作用域內得出了parent變數的值,生成這樣的節點:{ tag: "div", children: [{ tag: "child", children: ['這是插入到預設插槽的內容 父元件的值', '這是插入到one插槽的內容 父元件的值'] }] }
子元件解析,
slot
作為一個佔位符,會被解析成一個函式,大概意思像 解析成下面{ tag: "div", children: [ '我在子元件裡面', _t('default'), // 匿名插槽,預設名稱為default _t('one') // 具名插槽,名稱為one ] }
_t函式需要傳入插槽名稱,預設是
default
,具名插槽則傳入name
,這個函式的作用,是把第一步解析得到的插槽節點拿到,然後返回解析後的節點,那麼子元件的節點就完整了,插槽也成功認了爹——div
標籤{ tag: "div", children: ['我在子元件裡面', '這是插入到預設插槽的內容 父元件的值', '這是插入到one插槽的內容 父元件的值'] }
作用域插槽
//子元件 : (假設名為:child)
<template>
<div class= 'child'>
<slot :value1='child1' :value2='child1'></slot>
<slot name='one' :value1='child2' :value2='child2'></slot>
</div>
</template>
new Vue({
el:'child',
data:{
child1: '子資料1',
child2: '子資料2'
}
})
//父元件:(引用子元件 child)
<template>
<div class='app'>
<child>
<template v-slot:default='slotde'>
插入預設 slot 中{{ slotde.value1 }}{{ slotde.value2 }}
</template>
<template v-slot:one='slotone'>
插入one slot 中{{ slotone.value1 }}{{ slotone.value2 }}
</template>
</child>
</div>
</template>
過程很複雜,這裡就通俗點講了,父元件先解析,遇到作用域插槽,會將此插槽封裝成一個函式儲存到子元素
child
下{ tag: "div", children: [{ tag: "child" scopeSlots:{ default (data) { // 記住這個data引數 return ['插入one slot 中插入預設 slot 中' + data.value1 + data.value2] }, one (data) { // 記住這個data引數 return ['插入one slot 中' + data.value1 + data.value2] } } }] }
2.輪到子元件解析了,這個時候_t函式又登場了,並且子元件將對應的插槽資料包裝成一個物件,傳進_t函式
{
tag: "div",
children: [
'我在子元件裡面',
_t('default',{value1: '子資料1', value2: '子資料1'}),
_t('one',{value1: '子資料2', value2: '子資料2'})
]
}
接下來就是_t內部執行,包裝後的物件被當做data引數傳入了scopeSlots中的對應的各個函式,解析成:
{
tag: "div",
children: [
'我在子元件裡面',
'插入預設 slot 中 子資料1 子資料1',
'插入one slot 中 子資料2 子資料2'
]
}
$slots
看到這,相信大家已經清楚了大概一個過程(雖然不是很詳細),那麼又有一個疑問了,這些解析後的節點VNode
物件,是存在哪兒呢?你總不能解析出來然後就扔了吧?肯定得找個地方存起來然後進行真實dom
的渲染,這個地方就是$slots
//子元件 : (假設名為:child)
<template>
<div class= 'child'>
<slot></slot>
<slot name='one'></slot>
<slot name='two'></slot>
<slot name='three'></slot>
</div>
</template>
new Vue({
el:'.child',
created () {
console.log(this.$slots) // 看看裡面有啥
}
})
//父元件:(引用子元件 child)
<template>
<div class= 'app'>
<child>
<template> 這是插入到預設插槽的內容 </template>
<template v-slot:"one"> 這是插入到one插槽的內容 </template>
<template v-slot:"two"> 這是插入到two插槽的內容 </template>
<template v-slot:"three"> 這是插入到three插槽的內容 </template>
</child>
</div>
</template>
console.log的結果:
看到這裡大家都懂了,$slots
是一個Map
,key
是各個插槽的名稱(匿名插槽的key
為default
),key對應的value都是各個插槽下面的VNode節點,具體VNode物件裡都是長什麼樣,大家可以自己輸出看看,裡面東西太多,我就不在這展示了。嘿嘿。
結語
我是林三心,一個熱心的前端菜鳥程式設計師。如果你上進,喜歡前端,想學習前端,那我們們可以交朋友,一起摸魚哈哈,摸魚群,加我請備註【思否】