Vue.js 你需要知道的 v-slot (譯)
面試官:v-slot 的實現原理是什麼?
自己先想一分鐘。

這篇文章假設你對元件的基礎知識有定義的瞭解,如果你對此還不熟悉,請先閱讀。
從 [email protected] 開始,Vue 為具名和範圍插槽引入了一個全新的語法,即我們今天要講的主角: v-slot
指令。目的就是想統一 slot
和 scope-slot
語法,使程式碼更加規範和清晰。既然有新的語法上位,很明顯, slot
和 scope-slot
也將會在 [email protected]
中徹底的跟我們說拜拜了。而從 [email protected]
開始,官方推薦我們使用 v-slot
來替代後兩者。
我們來一點一點說起吧。文筆有限,不對之處請留言斧正!
具名插槽
在 2.6.0+ 中已棄用
先前,我們使用具名插槽來自定義模板內容,例如,一個假設的 <base-layout>
元件的模板如下:
<div class="container"> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div> 複製程式碼
在向具名插槽提供內容的時候,我們可以在一個父元件的 <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> 複製程式碼
或者直接用在一個普通的元素上:
<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> 複製程式碼
上述兩個示例渲染出來的 HTML 都將會是:
<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
指令改寫上面的栗子:
<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> 複製程式碼
就是這麼簡單,插槽的名字現在通過 v-slot:slotName
這種形式來使用。
Tips:沒有名字的 <slot>
隱含有一個 "default"
名稱
例如,上面的預設插槽,如果你想顯示呼叫的話,可以這樣:
<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> 複製程式碼
無論哪種方式,上面的程式碼都將輸出為下面程式碼:
<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
只能新增到 <template>
或自定義元件上,這點與棄用的 slot 屬性不同
作用域插槽
在 2.6.0+ 中已棄用
有時候,我們想在父元件中訪問子元件內部的一些可用資料。例如,假設有一個下面模板的 <current-user>
元件:
<span> <slot>{{ user.lastName }}</slot> </span> 複製程式碼
我們可能想用使用者的名字來替換點插槽裡面的姓,於是我們這樣寫:
<current-user> {{ user.firstName }} </current-user> 複製程式碼
很不幸,上面這段程式碼不能如你預期那樣工作,因為當前程式碼的作用域環境是在父元件中,所以它訪問不了 <current-user>
內部的資料。
為了解決這個, 我們可以在 <current-user>
內部的 <slot>
元素上動態繫結一個 user
物件屬性:
<span> <!-- 完整 v-bind:user 下面是簡寫形式 --> <slot :user="user"> {{ user.lastName }} </slot> </span> 複製程式碼
繫結到 <slot>
元素上的屬性我們稱之為 slot props 。現在,在父作用域中,我們可以通過 slot-scope
來訪問 user
資料了:
<current-user> <template slot-scope="slotProp"> {{ slotProp.user.firstName }} </template> </current-user> 複製程式碼
同樣的,我們使用 v-slot
重構上面的程式碼:
<current-user> <template v-slot:default="slotProps"> {{ slotProps.user.firstName }} </template> </current-user> 複製程式碼
或者直接作用在 <current-user>
上的寫法:
<!-- 省略預設插槽名字 --> <current-user v-slot="slotProp"> {{ slotProp.user.firstName }} </current-user> <!-- 顯示呼叫預設插槽名字 --> <current-user v-slot:default="slotProp"> {{ slotProp.user.firstName }} </current-user> 複製程式碼
在這個栗子中,我們選擇 slotProp
作為我們的 slot props 名字,但你可以使用你喜歡的任何名字。
單個預設插槽的縮寫形式
在上述情況下,當且僅當提供了預設插槽內容時,我們可以使用 v-slot
直接作用在元件上:
<current-user v-slot:default="slotProps"> {{ slotProps.user.firstName }} </current-user> 複製程式碼
我們可以簡化上面的的預設插槽寫法:
<current-user v-slot="slotProps"> {{ slotProps.user.firstName }} </current-user> 複製程式碼
請注意了,預設插槽的縮寫語法不能與具名插槽混用:
<!-- 控制檯將報警告:--> <!-- To avoid scope ambiguity, the default slot should also use <template> syntax when there are other named slots. --> <!-- 意思就是說,為了避免作用域模糊 --> <!-- 當有其他具名插槽時,預設插槽也應當使用 '<template>' 模板語法 --> <current-user v-slot="slotProps"> {{ slotProps.user.firstName }} <template v-slot:other="otherSlotProps"> slotProps is NOT available here </template> </current-user> 複製程式碼
於是,上面的程式碼,我們改寫成:
<current-user> <!-- 兩種寫法均可 --> <!--<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> 複製程式碼
插槽內容的結構賦值
在 Vue 程式碼內部,我們傳遞的 slotProps 其實就是函式的一個單一引數:
function (slotProps) { // ... slot content ... } 複製程式碼
這也就意味著 v-slot
的值只要滿足函式引數定義的 JavaScript 表示式的都可以接受。因此,在支援的環境(單檔案或現代瀏覽器)中,你還可以使用 ES2015 解構語法來提取特定的插值內容,例如:
<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> 複製程式碼
動態插槽名稱
2.6.0+ 新增
動態指令引數 也適用於 v-slot
,允許我們定義動態插槽名稱:
<base-layout> <template v-slot:[dynamicSlotName]> ... </template> </base-layout> 複製程式碼
命名插槽簡寫
2.6.0+ 新增
與 v-on
和 v-bind
類似, v-slot
也有一個簡寫,即使用 #
代替 v-slot
。例如, v-slot:header
簡寫成 #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> 複製程式碼
和其他指令一樣,只有在提供引數時才能使用簡寫形式,下面的寫法是無效的:
<!-- 將會觸發一個控制檯警告 --> <current-user #="{ user }"> {{ user.firstName }} </current-user> 複製程式碼
也就是說,如果你想使用簡寫語法,則務必指定插值的名字:
<current-user #default="{ user }"> {{ user.firstName }} </current-user> 複製程式碼
參考
- v-slot設計原理看這裡
- Vue中文文件更新的慢,英文文件看這裡
結尾!
囉嗦了這麼多,希望看到的同學或多或少有點收穫吧。不對的地方還請留言指正,不勝感激。俗話說, 三人行則必有我師! 希望更多熱衷於 Vue 的小夥伴能聚在一起交流技術!下面是我維護的一個Q群,歡迎掃碼進群哦,讓我們一起交流學習吧。也可以加我個人微信:G911214255 ,備註 掘金
即可。
