Vue新指令:v-slot
slot
是Vue元件的一個重要機制,因為它使得完全解耦的元件之間可以靈活地被組合。在《Vue元件內容分發》和《Vue的作用域插槽》文章中我們深入的學習了 slot
怎麼在Vue中的使用,但在Vue 3.0版本為 slot
引入了一套全新的模版語法。為了更好的從 2.x
過渡到 3.0
,Vue的 v2.6
版本引入了新的 slot
語法,即 v-slot
。接下來我們來學習新指令 v-slot
的使用。
v-slot
指令簡介
在 v2.6
中,我們為具名插槽和作用域插槽引入了一個新的統一語法,即 v-slot
。它取代了 slot
和 slot-scope
這兩個目前已被廢棄但未被移除且仍在 文件 中的特性。這是一個較重大的改變,主要包含了:
-
v-slot
指令結合了slot
和slot-scope
的功能 -
scoped slots
的簡寫
有關於 v-slot
指令形成的討論過程可以閱讀 RFC-0001 和 RFC-0002 中的描述。
回顧 slot
的使用
從官網上我們可以獲知,Vue中的 slot
主要分為: 單個插槽 、 具名插槽 和 作用域插槽 三種。而其使用也較為簡單,比如我們有一個 SlotDemo
元件,可以在該元件中通過 <slot>
元素作為承載分發內容的出口:
<!-- SlotDemo.vue --> <template> <div class="slot"> <slot /> </div> </template> <script> export default { name: 'SlotDemo', } </script>
然後它允許你像下面這樣使用 SlotDemo
元件,在使用該元件時,可以通過 <slot>
承載你想要的任何內容:
<!-- App.vue --> <SlotDemo> <div class="box"> <h1>Slot Demo</h1> <p>This is slot demo!</p> </div> </SlotDemo>
渲染出來的內容可以看出, div.box
中的內容替代了 <slot>
(也可以理解為該內容插入到了 slot
)中:
使用 slot
的時候,我們還可以為其設定一個預設的內容,也就是說沒有提供內容的時候被渲染。比如下面這個 SubmitButton
元件中,預設情況下該按鈕顯示的文字內容是" 提交 ",這個時候可以在 <slot>
標籤內設定其內容為“提交”:
<!-- SubmitButton.vue --> <template> <button> <slot>提交</slot> </button> </template> <script> export default { name: 'SubmitButton', } </script>
當我們引用 SubmitButton
並不給 slot
插入任何內容時:
<!-- App.vue --> <SubmitButton />
這個時候渲染出來的內容就是 slot
標籤中的預設內容:
<button data-v-24449ecc="">提交</button>
如果我們想給按鈕換成別的內容時,我們可以像下面這樣使用:
<SubmitButton>儲存</SubmitButton>
這個插入到 slot
中的內容“儲存”會替代 slot
中預設的內容“提交”:
<button data-v-24449ecc="">儲存</button>
最圖如下圖所示:
除了 單個插槽 之外,還可以使用 具名插槽 ,具名插槽的簡單用法就是給 slot
顯式的設定一個 name
值,比如下面這個 BaseLayout
元件:
<!-- BaseLayout.vue --> <template> <div class="container"> <header> <slot name="header">我們希望把頁頭放在這裡~</slot> </header> <main> <slot>我們希望把主要內容放在這裡</slot> </main> <footer> <slot name="footer">我們希望把頁尾放在這裡</slot> </footer> </div> </template> <script> export default { name: 'BaseLayout', } </script>
呼叫具名插槽時,在對應的標籤上使用 slot="name"
,比如下面這個簡單的用例:
<!-- App.vue --> <BaseLayout> <h1 slot="header">我是一個頁頭</h1> <p slot="footer">©w3cplus</p> </BaseLayout>
有關於Vue 2.6 版本之前 slot
更詳細的使用可以閱讀早前的學習筆記 《Vue元件內容分發》。
slot
還有另一個特性,那就是作用域插槽,用一個簡單的示例來描述作用插槽的使用。先建立一個 ColorList
元件:
<!-- ColorList.vue --> <template> <div class="color-list"> <h2>{{ title }}</h2> <div class="list"> <div class="list-item" v-for="(item, index) in items" :key="index"> <slot v-bind="item"></slot> </div> </div> </div> </template> <script> export default { name: 'ColorList', props: { title: { type: String, default: 'Colors' }, items: { type: Array } } } </script> <!-- App.vue --> <template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png"> <ColorList :items="colors" title="Colors"> <template scope="color"> <div :style="{background: color.hex}">{{ color.name }}</div> </template> </ColorList> </div> </template> <script> import ColorList from './components/ColorList' export default { name: 'app', components: { ColorList }, data () { return { colors: [ { name: 'Yellow', hex: '#F4D03F', }, { name: 'Green', hex: '#229954' }, { name: 'Purple', hex: '#9B59B6' } ] } } } </script>
有關於 Vue 2.6 之前作用域插槽更詳細的介紹可以閱讀早前整理的《Vue的作用域插槽》一文。
上面示例涉及到的Demo程式碼,可以從 [app-v-slot](//github.com/vuedemos/app-v-slot)
中的 step1
分支中獲取。
v-slot
的使用
前面再次回顧了 Vue 2.6 版本之前的 slot
的使用,知道Vue中的 slot
主要有 單個插槽 、 具名插槽 和 作用域插槽 三種。在Vue 2.6版本起對 slot
有所更新,已經廢棄了 slot
和 slot-scope
特性。從字面面上的理解就是Vue之後 slot
和 slot-scope
語法有所更改。自己在V3.0.1版本中親測下面的使用( step1
也是基於該版本測試的),但接下來,主要看 v-slot
的使用。
同樣先來看單個插槽,即 沒有在 <slot>
標籤中顯式的設定 name
值 。比如 SlotDemo
這個元件:
<!-- SlotDemo.vue --> <template> <div class="slot-demo"> <slot>我是一個slot</slot> </div> </template>
正如前面所述, slot
標籤中並沒有顯式設定 name
值,而且在 slot
標籤中提供了一個預設的內容。我們在使用該元件時,可以通過 v-slot:default
來呼叫未顯式設定 name
的 slot
:
<!-- App.vue --> <template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png"> <SlotDemo /> <SlotDemo v-slot:default> <p>v-slot:default 的使用</p> </SlotDemo> </div> </template>
但一般建議 v-slot:default
新增到 <template>
上:
<!-- App.vue --> <SlotDemo> <template v-slot:default> <h3>v-slot:default</h3> </template> </SlotDemo>
同樣能正常的渲染出我們預期的結果。
一個不帶name的<slot>出口會帶有隱含的名字default。
另外這裡有一個特殊之處, 當被提供的內容只有預設的插槽時,元件的標籤上才可以被當作插槽的模板來使用,也就是說 v-slot
可以直接使用在元件上 。正如上面的示例所示:
<SlotDemo v-slot:default> <p>v-slot:default 的使用</p> </SlotDemo>
接著來看一個具名插槽的示例,同樣拿 BaseLayout
元件來舉例:
<!-- BaseLayout.vue --> <template> <div class="container"> <header> <slot name="header">Header Content</slot> </header> <main> <slot>Main Content</slot> </main> <footer> <slot name="footer">Footer Content</slot> </footer> </div> </template> <script> export default { name: 'BaseLayout' } </script>
在 v2.6
之後使用也是採用 v-slot
:
<!-- V2.6版本之前 具名插槽的使用 --> <BaseLayout> <h1 slot="header">Vue 2.6之前具名插槽</h1> <p>我是頁面的主內容</p> <p slot="footer">©w3cplus</p> </BaseLayout> <!-- V2.6之後 具名插槽的使用 --> <BaseLayout> <template v-slot:header> <h1>Vue 2.6之後具名插槽</h1> </template> <template v-slot:default> <p>我是頁面的主內容</p> </template> <template v-slot:footer> <p>©w3cplus</p> </template> </BaseLayout>
在 <template>
元素中的所有內容都會被傳入相應的插槽中。另外,沒有任何被包裹在帶有 v-slot
的 <template>
中的內容都會被視為預設插槽的內容。然而,為了使用更具規範或明確一些,仍然可以在一個 <template>
中包裹預設插槽的內容,同時使用 v-slot:default
來表示。
v-slot
和 v-on
和 v-bind
類似,也可以縮寫,即 把引數之前的所有內容( v-slot:
)替換為字元 #
。例如,上面的示例我們可以改寫成:
<BaseLayout> <template #header> <h1>Vue 2.6之後具名插槽</h1> </template> <template #default> <p>我是頁面的主內容</p> </template> <template #footer> <p>©w3cplus</p> </template> </BaseLayout>
接下來,我們再一起來看看作用域插槽中怎麼來使用 v-slot
。同樣拿一個 List
元件來舉例,並且它暴露了一個過濾後的列表資料作為它的作用域插槽。
<!-- List.vue --> <template> <div class="list"> <div class="item" v-for="(item, index) in items" :key="index"> <slot v-bind="item" /> </div> </div> </template> <script> export default { name: 'List', props: { items: { type: Array, } } } </script>
我們可以這樣來呼叫 List
元件:
<!-- App.vue --> <!-- V2.6版本前 --> <List :items="colors"> <template slot-scope="color" slot="default"> <div :style="{backgroundColor: color.hex}"> {{ color.name }}</div> </template> </List> <!-- V2.6版本後 --> <List :items="colors" v-slot="color"> <div :style="{backgroundColor: color.hex}"> {{ color.name }}</div> </List> <script> import List from './components/List' export default { name: 'app', components: { List }, data () { return { colors: [ { name: 'Yellow', hex: '#F4D03F'}, { name: 'Green', hex: '#229954' }, { name: 'Purple', hex: '#9B59B6' } ] } } } </script>
從示例上的程式碼可以看出,使用 v-slot
,避免了額外的巢狀。另外 v-slot
指令也提供了一個繫結 slot
和 scope-slot
指令的方式,但需要使用一個 :
來分割它們。
有關於 v-slot
示例的相關程式碼,可以把分支切換到 step2
。
小結
在 2.6.0 中,我們為具名插槽和作用域插槽引入了一個新的統一的語法 (即 v-slot
指令)。它取代了 slot
和 slot-scope
這兩個目前已被廢棄但未被移除且仍在文件中的特性。這篇文章主要介紹了Vue中 slot
新老語法兩個版本的使用。希望對大家有所幫助。有關於更詳細的介紹可以閱讀 Vue官網提供的教程 。
擴充套件閱讀
- Vue插槽 (官網)
- Vue插槽廢棄的語法
- New v-slot directive in Vue.js 2.6.0
- Vue 插槽新語法討論過程: RFC-0001 和 RFC-0002
- Vue元件內容分發
- Vue的作用域插槽