Vue.js 你需要知道的 v-slot (譯)
面試官:談?wù)?v-slot 的作用?
自己先想一分鐘。

這篇文章假設(shè)你對(duì)組件的基礎(chǔ)知識(shí)有定義的了解,如果你對(duì)此還不熟悉,請(qǐng)先閱讀。
從 vue@2.6.x 開(kāi)始,Vue 為具名和范圍插槽引入了一個(gè)全新的語(yǔ)法,即我們今天要講的主角:v-slot 指令。目的就是想統(tǒng)一 slot 和 scope-slot 語(yǔ)法,使代碼更加規(guī)范和清晰。既然有新的語(yǔ)法上位,很明顯,slot 和 scope-slot 也將會(huì)在 vue@3.0.x 中徹底的跟我們說(shuō)拜拜了。而從 vue@2.6.0 開(kāi)始,官方推薦我們使用 v-slot 來(lái)替代后兩者。
我們來(lái)一點(diǎn)一點(diǎn)說(shuō)起吧。文筆有限,不對(duì)之處請(qǐng)留言斧正!
具名插槽
在 2.6.0+ 中已棄用
先前,我們使用具名插槽來(lái)自定義模板內(nèi)容,例如,一個(gè)假設(shè)的 <base-layout> 組件的模板如下:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
在向具名插槽提供內(nèi)容的時(shí)候,我們可以在一個(gè)父組件的 <template> 元素上使用 slot 特性:
<base-layout>
<template 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 slot="footer">
<p>Here's some contact info</p>
</template>
</base-layout>
或者直接用在一個(gè)普通的元素上:
<base-layout>
<h1 slot="header">Here might be a page title</h1>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<p slot="footer">Here's some contact info</p>
</base-layout>
上述兩個(gè)示例渲染出來(lái)的 HTML 都將會(huì)是:
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>
我們可以使用 v-slot 指令改寫(xiě)上面的栗子:
<base-layout>
<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's some contact info</p>
</template>
</base-layout>
就是這么簡(jiǎn)單,插槽的名字現(xiàn)在通過(guò) v-slot:slotName 這種形式來(lái)使用。
Tips: 沒(méi)有名字的
<slot>隱含有一個(gè)"default"名稱
例如,上面的默認(rèn)插槽,如果你想顯示調(diào)用的話,可以這樣:
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<template v-slot:default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
無(wú)論哪種方式,上面的代碼都將輸出為下面代碼:
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>
!!!請(qǐng)注意,
v-slot只能添加到<template>或自定義組件上,這點(diǎn)與棄用的 slot 屬性不同
作用域插槽
在 2.6.0+ 中已棄用
有時(shí)候,我們想在父組件中訪問(wèn)子組件內(nèi)部的一些可用數(shù)據(jù)。例如,假設(shè)有一個(gè)下面模板的 <current-user> 組件:
<span>
<slot>{{ user.lastName }}</slot>
</span>
我們可能想用用戶的名字來(lái)替換掉插槽里面的姓,于是我們這樣寫(xiě):
<current-user>
{{ user.firstName }}
</current-user>
很不幸,上面這段代碼不能如你預(yù)期那樣工作,因?yàn)楫?dāng)前代碼的作用域環(huán)境是在父組件中,所以它訪問(wèn)不了 <current-user> 內(nèi)部的數(shù)據(jù)。
為了解決這個(gè), 我們可以在 <current-user> 內(nèi)部的 <slot> 元素上動(dòng)態(tài)綁定一個(gè) user 對(duì)象屬性:
<span>
<!-- 完整 v-bind:user 下面是簡(jiǎn)寫(xiě)形式 -->
<slot :user="user">
{{ user.lastName }}
</slot>
</span>
綁定到 <slot> 元素上的屬性我們稱之為 slot props。現(xiàn)在,在父作用域中,我們可以通過(guò) slot-scope 來(lái)訪問(wèn) user 數(shù)據(jù)了:
<current-user>
<template slot-scope="slotProp">
{{ slotProp.user.firstName }}
</template>
</current-user>
同樣的,我們使用 v-slot 重構(gòu)上面的代碼:
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
或者直接作用在 <current-user> 上的寫(xiě)法:
<!-- 省略默認(rèn)插槽名字 -->
<current-user v-slot="slotProp">
{{ slotProp.user.firstName }}
</current-user>
<!-- 顯示調(diào)用默認(rèn)插槽名字 -->
<current-user v-slot:default="slotProp">
{{ slotProp.user.firstName }}
</current-user>
在這個(gè)栗子中,我們選擇
slotProp作為我們的 slot props 名字,但你可以使用你喜歡的任何名字。
單個(gè)默認(rèn)插槽的縮寫(xiě)形式
在上述情況下,當(dāng)且僅當(dāng)提供了默認(rèn)插槽內(nèi)容時(shí),我們可以使用 v-slot 直接作用在組件上:
<current-user v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</current-user>
我們可以簡(jiǎn)化上面的的默認(rèn)插槽寫(xiě)法:
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
</current-user>
請(qǐng)注意了,默認(rèn)插槽的縮寫(xiě)語(yǔ)法不能與具名插槽混用:
<!-- 控制臺(tái)將報(bào)警告:-->
<!-- To avoid scope ambiguity, the default slot should also use <template> syntax when there are other named slots. -->
<!-- 意思就是說(shuō),為了避免作用域模糊 -->
<!-- 當(dāng)有其他具名插槽時(shí),默認(rèn)插槽也應(yīng)當(dāng)使用 '<template>' 模板語(yǔ)法 -->
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
<template v-slot:other="otherSlotProps">
slotProps is NOT available here
</template>
</current-user>
于是,上面的代碼,我們改寫(xiě)成:
<current-user>
<!-- 兩種寫(xiě)法均可 -->
<!--<template v-slot="slotProps">
{{ slotProps.user.firstName }}
</template>-->
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
<template v-slot:other="otherSlotProps">
...
</template>
</current-user>
插槽內(nèi)容的解構(gòu)賦值
在 Vue 代碼內(nèi)部,我們傳遞的 slotProps 其實(shí)就是函數(shù)的一個(gè)單一參數(shù):
function (slotProps) {
// ... slot content ...
}
這也就意味著 v-slot 的值只要滿足函數(shù)參數(shù)定義的 JavaScript 表達(dá)式的都可以接受。因此,在支持的環(huán)境(單文件或現(xiàn)代瀏覽器)中,你還可以使用 ES2015 解構(gòu)語(yǔ)法來(lái)提取特定的插值內(nèi)容,例如:
<current-user v-slot="{ user }">
{{ user.firstName }}
</current-user>
代碼看起來(lái)更簡(jiǎn)潔對(duì)吧。我們還可以重命名解構(gòu)變量:
<current-user v-slot="{ user: person }">>
{{ person.firstName }}
</current-user>
這給了我們很多自由操作的空間,你甚至可以自定義回退內(nèi)容,以便在未定義插值情況下使用:
<current-user v-slot="{ user = { firstName: 'Guest' } }">>
{{ user.firstName }}
</current-user>
動(dòng)態(tài)插槽名稱
2.6.0+ 新增
動(dòng)態(tài)指令參數(shù) 也適用于 v-slot ,允許我們定義動(dòng)態(tài)插槽名稱:
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
</base-layout>
命名插槽簡(jiǎn)寫(xiě)
2.6.0+ 新增
與 v-on 和 v-bind 類(lèi)似,v-slot 也有一個(gè)簡(jiǎn)寫(xiě),即使用 # 代替 v-slot。例如, v-slot:header 簡(jiǎn)寫(xiě)成 #header:
<base-layout>
<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's some contact info</p>
</template>
</base-layout>
和其他指令一樣,只有在提供參數(shù)時(shí)才能使用簡(jiǎn)寫(xiě)形式,下面的寫(xiě)法是無(wú)效的:
<!-- 將會(huì)觸發(fā)一個(gè)控制臺(tái)警告 -->
<current-user #="{ user }">
{{ user.firstName }}
</current-user>
也就是說(shuō),如果你想使用簡(jiǎn)寫(xiě)語(yǔ)法,則務(wù)必指定插值的名字:
<current-user #default="{ user }">
{{ user.firstName }}
</current-user>
參考
- v-slot設(shè)計(jì)細(xì)節(jié)看這里
- Vue中文文檔更新的慢,英文文檔看這里
結(jié)語(yǔ)
看了那么多 相信也有同學(xué)和我一樣有點(diǎn)暈...(應(yīng)該只有我有點(diǎn)暈)
我們先要捋清楚思路2個(gè)概念
1.父組件要插入子組件的具名插槽(slot)
2.父組件需要用到子組件的數(shù)據(jù) (怎么玩看你)
我們先來(lái)看第一個(gè)
1
//父組件
<child>
<template v-slot:header> 我是父組件插入子組件了</template>
??????也可以簡(jiǎn)寫(xiě)
<template #header> 我是父組件插入子組件了</template>
</child>
//子組件
<template>
<div>
<slot name ="header">我是子組件原來(lái)的數(shù)據(jù)</slot>
</div>
</template>
以上的代碼是父組件插入子組件的插槽
2
//父組件
<child v-slot="slotProp">
<template v-slot:header> {{slotProp.name}}</template>
</child>
//子組件
<template>
<div>
<slot name ="header" :slotProp="{name: '我是傳出去的數(shù)據(jù)'}">我是子組件原來(lái)的數(shù)據(jù)</slot>
</div>
</template>
這樣我們就是把"我是傳出去的數(shù)據(jù)"插入到了子組件的數(shù)據(jù)
注意區(qū)別對(duì)待v-slot="" 和v-slot:name
=和:的區(qū)別 一個(gè)是slot的name 一個(gè)是父組件獲取子組件的數(shù)據(jù)
以下還有個(gè)代碼片段僅供參考
代碼片段
項(xiàng)目使用element-Ui的 請(qǐng)不要升級(jí)2.6+ 詳情見(jiàn)圖片
注意事項(xiàng).png
