概要
Vue3(其實從2.6開始)中引入了一個新的指令v-slot
,用來表示具名插槽和默認插槽
基礎示例
<!-- default slot -->
<foo v-slot="{ msg }">
{{ msg }}
</foo>
<!-- named slot -->
<foo>
<template v-slot:one="{msg}">
{{ msg }}
</template>
</foo>
為什么要這么做
在v2.5中介紹了slot-scope
,可以直接在插槽元素上使用。
<foo>
<div slot-scope="{ msg }">
{{ msg }}
</div>
</foo>
同樣的,我們也可以在組件上這樣使用。
<foo>
<bar slot-scope="{ msg }">
{{ msg }}
</bar>
</foo>
然而,上面的示例會導致一個問題:slot-scope
的位置不能清晰的反映出這個作用域變量是哪一個 組件提供的。上面例子中slot-scope
是寫在<bar>
組件上,但是實際上這個作用域變量的定義是有<foo>
組件的默認插槽提供的。
當嵌套的層級越深,情況就會變得越糟。
<foo>
<bar slot-scope="foo">
<baz slot-scope="bar">
<div slot-scope="baz">
{{ foo }} {{ bar }} {{ baz }}
</div>
</baz>
</bar>
</foo>
像上面這種情況,我們無法立即分辨出模板上作用域變量是由那一個組件提供的。
細節設計
引入一個新的指令v-slot
我們可以在slot容器<template>
上使用v-slot
來表示一個傳入組件的插槽,通過指令參數來表示插槽的名稱。
<foo>
<template v-slot:header>
<div class="header"></div>
</template>
<template v-slot:body>
<div class="body"></div>
</template>
<template v-slot:footer>
<div class="footer"></div>
</template>
</foo>
作用域插槽的內部工作原理是將插槽的內容包裹在一個函數里
function(slotProps){
// 插槽內容
}
這就意味着v-slot
的值實際上可以是任何能夠作為函數定義中的參數的Javascript表達式,所以在支持的環境下 (單文件組件或現代瀏覽器),你也可以使用 ES2015 解構來傳入具體的插槽 prop,如下:
<foo>
<template v-slot:header="{ msg }">
<div class="header">
Message from header slot: {{ msg }}
</div>
</template>
</foo>
v-slot
可以直接用在組件上,如果沒有參數,則表示默認的作用域插槽,傳遞給默認插槽的屬性應該作為變量在其屬性值聲明。
<foo v-slot="{ msg }">
{{ msg }}
</foo>
新舊語法對比
-
對於常用的場景(只有一個默認的作用域插槽)
<foo v-slot="{msg}">{{msg}}</foo>
-
作用域變量與組件之間的聯系更加的清晰
<!-- old --> <foo> <bar slot-scope="foo"> <baz slot-scope="bar"> <div slot-scope="baz"> {{ foo }} {{ bar }} {{ baz }} </div> </baz> </bar> </foo>
使用新語法也可以達到同樣的效果
<foo v-slot="foo"> <bar v-slot="bar"> <baz v-slot="baz"> {{ foo }} {{ bar }} {{ baz }} </baz> </bar> </foo>
注意:組件提供的作用域變量依然聲明在當前組件上,新的語法更清晰的展示出組件與作用域變量的關系。(父級模板里的所有內容都是在父級作用域中編譯的;子模板里的所有內容都是在子作用域中編譯的。)
動態插槽名
動態指令參數也可以用在 v-slot
上,來定義動態的插槽名:
<foo>
<template v-slot:[slotName]>
...
</template>
</foo>
插槽指令的縮寫
和 v-bind
和v-on
相似,縮寫只有在存在參數時才生效,這就意味着v-slot
沒有參數時不能使用#=
,對於默認插槽,可以使用#default
來代替v-slot
<!-- full syntax -->
<foo>
<template v-slot:header="{ msg }">
Message from header: {{ msg }}
</template>
<template v-slot:footer>
A static footer
</template>
</foo>
<!-- shorthand -->
<foo>
<template #header="{ msg }">
Message from header: {{ msg }}
</template>
<template #footer>
A static footer
</template>
</foo>
<foo v-slot="{ msg }">
{{ msg }}
</foo>
<foo #default="{ msg }">
{{ msg }}
</foo>
更多的用例對比
默認的文本作用域插槽
<!-- old -->
<foo>
<template slot-scope="{ msg }">
{{ msg }}
</template>
</foo>
<!-- new -->
<foo v-slot="{ msg }">
{{ msg }}
</foo>
默認的含DOM節點的插槽
<!-- old -->
<foo>
<div slot-scope="{msg}">
{{msg}}
</div>
</foo>
<!-- new -->
<foo v-slot="{msg}">
<div>
{{msg}}
</div>
</foo>
嵌套的默認插槽
<!-- old -->
<foo>
<bar slot-scope="foo">
<baz slot-scope="bar">
<template slot-scope="baz">
{{ foo }} {{ bar }} {{ baz }}
</template>
</baz>
</bar>
</foo>
<!-- new -->
<foo v-slot="foo">
<bar v-slot="bar">
<baz v-slot="baz">
{{ foo }} {{ bar }} {{ baz }}
</baz>
</bar>
</foo>
命名插槽
<!-- old -->
<foo>
<template slot="one" slot-scope="{ msg }">
text slot: {{ msg }}
</template>
<div slot="two" slot-scope="{ msg }">
element slot: {{ msg }}
</div>
</foo>
<!-- new -->
<foo>
<template v-slot:one="{ msg }">
text slot:{{msg}}
</template>
<template v-slot:two="{msg}">
<div>
element slot: {{msg}}
</div>
</template>
</foo>
嵌套 & 命名 / 默認
<!-- old -->
<foo>
<bar slot="one" slot-scope="one">
<div slot-scope="bar">
{{ one }} {{ bar }}
</div>
</bar>
<bar slot="two" slot-scope="two">
<div slot-scope="bar">
{{ two }} {{ bar }}
</div>
</bar>
</foo>
<!-- new -->
<foo>
<template v-slot:one="one">
<bar v-slot="bar">
<div>{{ one }} {{ bar }}</div>
</bar>
</template>
<template v-slot:two="two">
<bar v-slot="bar">
<div>{{ two }} {{ bar }}</div>
</bar>
</template>
</foo>