什麼是插槽
插槽(
slot
)通俗的了解就是“占坑”,在元件模闆中占有位置,當使用該元件的時候,可以指定各個坑的内容。也就是我們常說的内容分發
值得一提的是,插槽這個概念并不是
Vue
提出的,而是
web Components
規範草案中就提出的,具體入門可以看 使用 templates and slots[1] ,
Vue
隻是借鑒了這個思想罷了
在 Vue 2.6.0 中,我們為具名插槽和作用域插槽引入了一個新的統一的文法 (即
v-slot
指令)。它取代了
slot
和
slot-scope
,這兩個目前已被廢棄但未被移除且仍在文檔中的 attribute
本文的例子基于 Vue 2.6.X,是以用的都是 v-slot 的文法。
本文
DEMO
已全部放到 Github[2] 和 沙箱[3] 中,供大家學習,如有問題,歡迎評論提出。
預設插槽
我們建立父元件
Parent
和子元件
Child
,結構如下:
父元件:
子元件:
父元件調用
Child
元件的時候,會在
Child
标簽中将内容傳入到子元件中的
标簽中,如下所示
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5yN1cTMjlTO5EWNiJDNxYzM4UmZ2YmYzUzMyQDOyUzY48CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
也就是最後的渲染結果如下:
後備内容
我們可以在子元件中的
中加入一些内容,像下面一樣
當父元件調用的時候, 子元件标簽内沒有相關的内容時候,
标簽内的内容就會生效,否則就不會渲染,可以了解就是個“備胎”
如父元件調用上面子元件:
結果如下:
具名插槽
當然,插槽可以不止一個,這個主要是為了能夠靈活的控制插槽的位置以及元件的抽象。我們可以通過在子元件的
slot
标簽中設定
name
屬性,然後在父元件中通過
v-slot:
(或者使用簡寫
#
) + 子元件
name
屬性值的方式指定要插入的位置。如果是預設插槽的話,
v-slot:default
即可
如下父元件:
子元件
需要留意的是,最後渲染的順序是以子元件的順序為主,也就是上面的例子,渲染出來如下:
作用域插槽
有時候,我們想在一個插槽中使用子元件的資料和事件,類似如下(注意:
user
是定義在
Child3
元件中的資料):
會直接報錯:
原因在于父元件取不到子元件的資料,這裡記住一個原則:父級模闆裡的所有内容都是在父級作用域中編譯的;子模闆裡的所有内容都是在子作用域中編譯的。
那我們怎樣才能擷取到子元件的資料或者事件呢?我們可以直接在子元件中通過
v-bind
的方式将資料或者事件傳遞給父元件中,如下所示
然後在父元件中的插槽内,通過類似
v-slot:default="slotProps"
接受子元件傳遞過來的資料
以上
slotProps
可以自定義,而且可以使用解構指派的文法
執行個體:解耦業務邏輯和視圖
我們經常會遇到一個場景,就是兩個元件的業務邏輯是可以複用的,但是視圖卻不一樣,比如我們經常會有類似切換開關的需求,功能包括:
- 關閉開關
- 打開開關
- 切換開關
- 開關關閉或者打開的時候不一樣的内容
我們可以很快的寫出它的一個
JS
業務邏輯代碼:
但是可能現在我的樣式一是這樣的
然而另外一個地方的樣式是這樣的(隻是舉個例子,現實可能更加的複雜,甚至有可能一些按鈕直接就隐藏掉了)
這個時候,插槽就派上了用場。上面提到作用域插槽可以将資料和事件從子元件傳遞給父元件,這就相當于對外暴露了接口。而且可以将
HTML
中的
DOM
以及
CSS
交給父元件(調用方)去維護,子元件通過
标簽插入的位置即可,主要邏輯如下:
子元件:
父元件:
我們現在采用的是單檔案的方式書寫的,實際上子元件還是會有相關的
HTML
結構,如何做到子元件完全不需要渲染自己的
HTML
呢?那得了解下無渲染元件的實作
進階:無渲染元件的實作
無渲染元件(renderless components)是指一個不需要渲染任何自己的
HTML
的元件。相反,它隻管理狀态和行為。它會暴露一個單獨的作用域,讓父元件或消費者完全控制應該渲染的内容。
Vue
中,提供了單檔案元件的寫法。像上面的示例一樣,我們始終還是在子元件中進行了一些渲染的操作,那如何做到真正的不渲染元件呢?
比如上面的
toggle
例子,我們已經做到了子元件暴露一個單獨的作用域,讓父元件或消費者完全控制應該渲染的内容。現在我們需要将單檔案中的
template
結構(
slot
标簽外層的
div
)完全交給父元件,但單檔案元件中
slot
标簽是不能作為
template
的根元素的
這個時候,我們需要了解一下 Vue 渲染函數(
render function
)
歸根結底,
Vue
及其所有的元件都隻是
JavaScript
。單檔案元件最後會被建構工具,如
webpack
,将
CSS
抽取形成一個檔案,其他的内容會被轉換成
JavaScript
,類似如下:
當然,這個不是它的最終形态,模闆編譯器會提取
template
屬性内容并将其内容編譯為
JavaScript
,然後通過
render
函數添加到元件對象中。最終形态應該是如下:
具體的渲染函數可參見官網[4],雖然寫
render
函數的成本會高一些,但是它的性能會比單檔案元件好很多。
以上的例子,隻有插槽的時候,我們隻需要在
render
函數中,使用
this.$scopedSlots.default
代替掉
标簽即可
代碼如下:
以上就可以做到子元件完全不渲染自己的
HTML
了
總結
本文介紹了一些
Vue
插槽的基本知識,包括
- 預設插槽
- 後備内容
- 具名插槽
- 作用域插槽
然後介紹了一下,如何通過插槽實作業務邏輯和視圖的解耦,再結合渲染函數實作真正的無渲染函數
本文
DEMO
已全部放到 Github[5] 和 沙箱[6] 中,供大家學習,如有問題,可以評論提出。
這麼用心了,求個贊,哈哈
希望對大家有所幫助~
往期優秀文章推薦
- 【Vue進階】——如何實作元件屬性透傳?[7]
- 前端應該知道的 HTTP 知識【金九銀十必備】[8]
- 最強大的 CSS 布局 —— Grid 布局[9]
- 如何用 Typescript 寫一個完整的 Vue 應用程式[10]
- 前端應該知道的web調試工具——whistle[11]
參考:
- Vue 插槽(slot)使用(通俗易懂)[12]
- vue 2.6 中 slot 的新用法[13]
- (譯)函數式元件在Vue.js中的運用[14]
- Building “Renderless” Vue Components[15]
參考資料
[1]
使用 templates and slots: https://developer.mozilla.org/zh-CN/docs/Web/Web_Components/Using_templates_and_slots
[2]
Github: https://github.com/GpingFeng/vue-slot
[3]
沙箱: https://codesandbox.io/s/hopeful-nash-id826?file=/src/main.js
[4]
官網: https://cn.vuejs.org/v2/guide/render-function.html
[5]
Github: https://github.com/GpingFeng/vue-slot
[6]
沙箱: https://codesandbox.io/s/hopeful-nash-id826?file=/src/main.js
[7]
【Vue進階】——如何實作元件屬性透傳?: https://juejin.im/post/6865451649817640968
[8]
前端應該知道的 HTTP 知識【金九銀十必備】: https://juejin.im/post/6864119706500988935
[9]
最強大的 CSS 布局 —— Grid 布局: https://juejin.im/post/6854573220306255880
[10]
如何用 Typescript 寫一個完整的 Vue 應用程式: https://juejin.im/post/6860703641037340686
[11]
前端應該知道的web調試工具——whistle: https://juejin.im/post/6861882596927504392
[12]
Vue 插槽(slot)使用(通俗易懂): https://juejin.im/post/6844903920037281805
[13]
vue 2.6 中 slot 的新用法: https://juejin.im/post/6844903885476200461
[14]
(譯)函數式元件在Vue.js中的運用: https://juejin.im/post/6844903752164442120
[15]
Building “Renderless” Vue Components: https://css-tricks.com/building-renderless-vue-components/