0%

Alex宅幹嘛ReVue-Day12

在我之前的文章 中,有紀錄了基本的元件插槽應用。
那在這邊就介紹更多進階的插槽使用情境。

Slot 簡介

我們可以在自訂的元件的 template 中塞入 的標籤,在這個標籤內部可以塞入你想加入這個元件的內容,就算是 HTML 標籤的內容也可以。
ex:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
---HTML---
<basic-component :title="title">
<a href="#">World</a>
</basic-component>

---JavaScript---
const BasicComponent = {
template: `<div>
{{ title }}
<slot></slot>
</div>`,
props: {
title: {
type: String,
requier: true
}
}
}
Vue.component('basic-component', BasicComponent)

以上這個範例,我們在 HTML 文本部分的元件中,加入了 a 標籤的內容,那這個 a 標籤的內容就會取代 basic-component 元件的 template 中的 標籤。

Compilation Scope

這部分是在說明 的作用域。
在元件中的 slot 可以取用與其所屬元件相同作用域的其他物件的屬性。但它不能調用其所屬元件的屬性。
ex:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
---HTML---
<div id="app">
<basic-component :title="title">
{{ msg }}
</basic-component>
</div>

---JavaScript---
const BasicComponent = {
template: `<div>
{{ title }}
<slot></slot>
</div>`,
props: {
title: {
type: String,
requier: true
}
},
data() {
msg: 'product'
}
}
Vue.component('basic-component', BasicComponent)

new Vue({
el:'#app',
data: {
msg: 'book'
}
})

上面這個範例你可以看到在 Vue 元件中有一個 msg 的屬性,而在 basic-component 中也有一個 msg 的屬性,那按照我面在上面的說明,slot 只能取用與其所屬元件相同作用域的屬性,
所以,它的結果會調用到 Vue 元件的 msg 屬性,所以,最終呈現的結果會是 book。 而其所屬元件 basic-component 的 msg 的屬性是取用不到的喔。
以上是 slot 的作用域特性要特別注意。

記得不要將上面這種情境和下面這種情境搞混囉
ex:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
---HTML---
<div id="app">
<basic-component :title="title">
Save
</basic-component>
</div>

---JavaScript---
const BasicComponent = {
template: `<div>
{{ title }}
<slot> {{ msg }} </slot> // 這會調用到 basic-component 自己的 msg 屬性
</div>`,
props: {
title: {
type: String,
requier: true
}
},
data() {
msg: 'product'
}
}
Vue.component('basic-component', BasicComponent)

你可以看到這邊這個範例,調用 msg 屬性,是寫在 basic-component 的 template 內部,所以,它會調用到 basic-component 自身的 msg,
而在上面那個例子 msg 是寫在 HTML 文本裡面,所以只能調用跟其所屬元件相同作用域的 Vue 元件的 msg 屬性。
這兩種作用域不要搞混了。 一個是父元件作用域,一個是子元件自己的作用域。

Fallback Content

這個部分是在說明 slot 預設值的設定。
ex:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
---HTML---
<basic-component></basic-component>

---JavaScript---
const BasicComponent = {
template: `<div>
{{ title }}
<slot>{{ msg }}</slot>
</div>`,
props: {
title: {
type: String,
requier: true
},
msg: {
type: String,
default: 'submit'
}
}
}
Vue.component('basic-component', BasicComponent)

以上的範例你可以看到在 我們沒有加入任何東西,如果此時你在該元件的 slot 標籤中有放入內容的話,則這個內容就會被當作預設內容呈現在畫面中,
所以,上面這個範例 slot 會呈現 BasicComponent 自己的成員屬性 msg 的內容。 反之,若在 中有放入內容的話, slot 的預設內容就會被取代掉。

Name Slots

這邊是在介紹有為 slot 取名的時候,要怎麼使用它們。
在 Fallback Content 的部分,我們並沒有為 slot 取名,所以,我們無法達成將特定內容塞到相對的 slot 中的效果。而利用為 slot 取名就可以達成這種效果囉。
ex:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
---HTML---
<basic-component>
<template v-slot:header>Header</template>
<template>My name is Landy</template>
<template v-slot:footer>Footer</template>
</basic-component>

---JavaScript---
const BasicComponent = {
template: `<div class="container">
<header>
<slot name="header">This is Header</slot>
</header>
<main>
<slot>This is main</slot>
</main>
<footer>
<slot name="footer">footer</slot>
</footer>
</div>`,
}

你可以看到以上的範例,我們有為兩個 slot 分別取名為 header 和 footer,所以,在 parent-component 的狀態下,我們在 template 標籤中加入 v-slot: 後面加入相對應的 slot 名稱後,
我們就可以將特定的內容塞到相對應的 slot 中囉。
而在 main 標籤裡面的 slot 我們就沒有為它特別命名,而在 Vue 的官方文件有提到,這類的 slot 名稱會預設為 default,所以,在 parent-component 的狀態下,那些沒有用 v-slot: 來決定
其內容要塞到哪一個 slot 的內容,就會自動被塞到這種沒有被命名的slot 中囉。

沒有放在指定 slot 中的內容

ex:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
---HTML---
<basic-component>
Landy
<template v-slot:header>Header</template>

<div>Thsi is DIV</div>
<a href="#">This is a tag</a>

<template v-slot:footer>Footer</template>
</basic-component>

---JavaScript---
const BasicComponent = {
template: `<div class="container">
<header>
<slot name="header">This is Header</slot>
</header>
<main>
<slot>This is main</slot>
</main>
<footer>
<slot name="footer">footer</slot>
</footer>
</div>`,
}

你可以看到上面範例中,在 parent-component 中,有一些內容都沒有被 template 包起來,那這種內容會跑到哪裡去呢?
答案是這些內容都會統一被塞到預設的 slot 中,也就是會被塞到沒有被命名的 slot 中,且呈現順序會是由上到下排列。

沒有預設 slot 的內容

今天我們在變換一下上面的範例,如果我們連預設的 slot 的沒有的話,那這些內容沒有被指定放入特定 slot 會跑到哪裡去呢?
ex:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
---HTML---
<basic-component>
Landy
<template v-slot:header>Header</template>

<div>Thsi is DIV</div>
<a href="#">This is a tag</a>

<template v-slot:footer>Footer</template>
</basic-component>

---JavaScript---
const BasicComponent = {
template: `<div class="container">
<header>
<slot name="header">This is Header</slot>
</header>
<main>
Main
</main>
<footer>
<slot name="footer">footer</slot>
</footer>
</div>`,
}

答案會是,在 parent-component 這些沒有被指定放入特定 slot 的內容會直接被移除掉,所以,此範例由上到下呈現的內容會是
Header
Main
Footer

如果有加入v-slot:default呢

如果今天在 parent-component 中,有一個 template 搭配 v-slot:default 的話,那上面範例中那些沒有被指定的內容還會出現嗎?
ex:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
---HTML---
<basic-component>
<a href="#">a tag</a>

<template v-slot:header>Header</template>

<template v-slot:default>
<p>This is default content</p>
</template>

<template v-slot:footer>Footer</template>
</basic-component>

---JavaScript---
const BasicComponent = {
template: `<div class="container">
<header>
<slot name="header">This is Header</slot>
</header>
<main>
<slot>This is Main</slot>
</main>
<footer>
<slot name="footer">footer</slot>
</footer>
</div>`,
}

以上的範例結果是,只會留下 v-slot:default 中的內容塞入 default slot,所以,在 parent-component 中的那個沒有被指定塞入特定 slot 的 a 連結內容,就會直接被移除掉囉。

Scoped Slot

在 slot 有提供一種方法來讓父層作用域可以調用到子層作用域的資料。
ex:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
---HTML---
<basic-component>
<template v-slot:header>Header</template>
<template v-slot:default="slotProps">{{ slotProps.user.first }}</template>
</basic-component>

---JavaScript---
const BasicComponent = {
template: `<div class="container">
<header>
<slot name="header">This is Header</slot>
</header>
<main>
<slot v-bind:user="UserData">{{ UserData.last }}</slot>
</main>
</div>`,
data () {
return {
UserData: {
first: 'Landy',
last: 'Chen'
}
}
}
}

在以上範例中,我們在子層的預設 slot 中,利用 v-bind 去綁定一個我們自訂義的屬性 user,而此屬性綁定的子層資料為 UserData。
此時,在父層部分,你可以看到,在 v-slot:default 的 parent-component 中,有額外再綁一個我們自訂義的參數叫 slotProps,而透過這個參數
我們就可以去調用我們在預設 slot 中傳出來的 user 屬性,接著再透過這個 user 屬性去使用子層的 UserData 的資料。

Scoped Slot 傳入 JS 表達式

上面的範例我們有看到怎麼使用 v-bind 來將子層資料傳出去給父層使用。
那在 Vue 中還有提供一個更簡便的方式來讓父層取用子曾傳出來的資料,就是用 ES6 的語法將綁定的資料傳出來,並用再父層額外命名一個 props 的名稱了。
ex:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
---HTML---
<basic-component>
<template v-slot:header="{ data }">{{ data.first }}</template>
<template>Main</template>
</basic-component>

===JavaScript---
const BasicComponent = {
template: `<div class="container">
<header>
<slot name="header" v-bind:data="UserData">{{ UserData.last }}</slot>
</header>
<main>
<slot>This is Main</slot>
</main>
</div>`,
data () {
return {
UserData: {
first: 'Landy',
last: 'Chen'
}
}
}
}

你可以看到在 parent-component 中我們用了v-slot:header=”{ data }” 的 es6 的語法,直接將我們在子層定義的傳出資料的 data 內容,直接取出來,
就可以直接取用裡面的內容囉。 可以讓程式碼看起來比較簡潔。

Dynamic Slot Names

Vue 有提供動態切換 slot 名稱的功能。
ex:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
---HTML---
<basic-component>
<template v-slot:header>Header</template>
<template v-slot:[DynamicSlot]> 5555 </template>
</basic-component>

---JavaScript---
const BasicComponent ={
template: `
<header>
<slot name="header">This is Header </slot>
</header>
<main>
<slot>This is Main </slot>
</main>
`
}

Vue.component('basic-component', BasicComponent)

new Vue({
el: '#app',
data: {
DynamicSlot: 'default'
}

})

你可以看到以上範例,我們在 basic-component 上的其中一個 template 綁上了 DynamicSlot ,那這個資料是由 Vue 物件來決定它的內容的,而這邊是把它設為 default ,所以
,在該 template 的內容,會被插入到 basic-component 的 template 中的預設 slot 中,也就是 This is Main 會被 5555 給取代掉。
那今天若把 DynamicSlot 改成 header 的話,你會發現原本在 basic-component 預計要塞入的 Header 內容,會直接被 5555 搶先取代掉了。
由以上這種表現可以知道, v-slot 綁定的動態 slot 的優先序比原本元件中的靜態 slot 還要高。