當我們利用組件實現項目需求時,我們有時候會希望像html中其他標籤那樣,可以在使用組件時插入我們的自定義內容。例如下面這個場景:我們定義了一個叫「<input-component>」的組件,在組件的模板中,我們添加了一些<input>標籤:
(為什麼我們不在模板內給它們一些<label>標籤或placeholder屬性作為內容提示呢?這是因為我們的目的是定義一個可以復用並能夠應用在不同地方的組件。)
我們需要在頁面顯示這個組件,並給出提示信息為:login。針對這樣的需求,如果我們直接在組件中間插入內容是無效的,login文本並不會被顯示出來。
這個時候我們需要使用vue提供的slot插槽來解決。
基本使用與後備內容
插槽是vue自定義的一個<slot>元素,我們可以簡單地在模板內想要顯示我們自定義內容的地方插入<slot>標籤,當我們使用組件時,就會達到顯示自定義內容的效果。
如果我們想要在不插入自定義內容時候顯示默認內容,我們可以在<slot>內部自定義後備內容。例如:
編譯作用域
在理解編譯作用於之前,先讓我們看一下這段代碼:
當我們使用組件的時候,我們所插入的自定義內容可能包含某個數據,上面代碼中,我們插入的內容中訪問了父級組件的user數據,毫無疑問是可以的,因為這是在父級模板中的內容,所以是在父級作用域中編譯的。但如果此時我們訪問的是定義在<input-component>組件裡的title數據,結果是訪問不到的,因為這屬於在父級作用域訪問子組件作用域的數據。解決的辦法是利用作用域插槽(下文中有介紹)。
具名插槽
我們也許會有插入多個內容的需求,並想把這些文本分模塊顯示在不同的部分,比如我們需要插入「header」「main」「footer」三個布局的內容,我們可以在模板中的指定部分分別插入<slot>標籤,為了區分,我們需要給這些插槽一個name的屬性,它們的值即是header、main、footer,這就是具名插槽。
使用該組件時,我們需要利用<template>和v-slot指令來關聯位置,v-slot的參數為<slot>標籤中name屬性的值。這樣我們就可以將插入的內容傳入相應的插槽裡。
如果我們還插入了其他內容,而這些內容沒有應用<template>和v-slot,則它們會被視為默認插槽(name=「default」)的內容。
和v-bind或v-on一樣,對於v-slot也同樣存在簡寫的語法,我們可以使用「#」來代替該指令,不過需要注意的是,它不能直接賦值(關於為什麼賦值可以參考下一部分的作用域插槽)如果v-slot沒有參數,需要寫成#default="propObjectName"。
作用域插槽
由於編譯作用域的限制,我們不能用插槽內容訪問子組件中的數據。但我們極有可能需要這樣的功能,所以vue中提供了作用域插槽來解決這個問題。
我們可以在slot中使用v-bind綁定一個自定義的屬性(例如下圖js代碼中的mytitle)來引入這個子組件數據(例如下圖js代碼中的title),然後在父組件中給v-slot指令賦值為一個自定義的對象變量(例如下圖html代碼中的slotProps),我們可以利用此對象變量顯示子組件中的數據(例如下圖html代碼中的slotProps.mytitle)。
ps:這跟利用Prop從父組件向下傳遞給子組件數據的方式有些相似之處。
作用域插槽原理是將插槽內容包裹在一個函數裡,而插槽prop名是這個函數的一個參數,結合上面的例子說明,就是slotProps是這個函數的參數。由於slotProps又是一個包含mytitle的對象,所以我們可以利用ES6的解構賦值語法,直接給v-slot傳遞mytitle而不使用slotProps: