一、什么是插槽?
vue.js官方文档上介绍:将 slot元素作为承载分发内容的出口。
也就是说,当子组件有一部分内容是根据父组件传递过来的dom进行显示时,可用slot。
二、共有三种插槽(加解构插槽)
1.匿名插槽(单个插槽、默认插槽)
没有设置name属性的插槽。
<slot>这是个匿名插槽(没有name属性),这串字符是匿名插槽的默认值。</slot>
可以放置在组件的任意位置。
一个组件中只能有一个匿名插槽。
匿名插槽只能作为没有slot属性的元素的插槽。
<div class="child">
<h1>子组件</h1>
<slot name="head">头部默认值</slot>
<slot name="body">主体默认值</slot>
<slot>这是个匿名插槽(没有name属性),这串字符是匿名插槽的默认值。</slot>
</div>
<div class="parent">
<h1>父组件</h1>
<child>
<p slot="body">我是主体</p>
<p>我是其他内容</p>
<p slot="footer">我是尾巴</p>
</child>
</div>
父组件
子组件
头部默认值 (具名插槽<slot name="head">的默认值被渲染,因为父组件没有为此插槽提供内容)
我是主体 (具名插槽<slot name="body">的默认值被覆盖)
我是其他内容 (匿名插槽的默认值被覆盖)注意:
1、<p slot="footer">我是尾巴</p> 插槽被丢弃了,因为子组件中没有<slot name="footer">的插槽与之匹配。
2、 如果子组件中的匿名插槽不存在,则<p>我是其他内容</p>也会被丢弃。
2.具名插槽
意思就是具有名字的插槽,名字通过属性name来定义。
<slot name="body">这是个具名插槽(有name属性),这串字符是具名插槽的默认值。</slot>
一个组件中可以有很多具名插槽,出现在不同的位置。
<!-- <base-layout>组件-->
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot> <!-- 一个不带 name 的 <slot> 出口会带有隐含的名字“default”。-->
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
除了上面那个例子中,把slot
直接用在普通标签或者<template>
上,更推荐在 <template>
元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供插槽名称,这样就可以定义插槽的内容了:
<base-layout>
<template v-slot:header>
<h1>我是头header</h1>
</template>
<p>我是main的内容111</p>
<p>我也是main的内容222</p>
<template v-slot:footer>
<p>我是footer</p>
</template>
</base-layout>
1、带有 v-slot 的<template>
元素中的所有内容都将会被传入相应的插槽。
2、任何没有被包裹在带有 v-slot 的 <template>
中的内容都会被视为默认插槽的内容。
如果你希望更明确一些,可以在一个 <template>
中包裹默认插槽的内容:
<base-layout>
<template v-slot:header>
<h1>我是头header</h1>
</template>
<template v-slot:default>
<p>我是main的内容111</p>
<p>我也是main的内容222</p>
</template>
<template v-slot:footer>
<p>我是footer</p>
</template>
</base-layout>
以上两种写法的渲染效果是一样的:
注意: v-slot 只能添加在 <template>
上。 (只有一种例外情况),请继续往下看。
3.作用域插槽
上面props的例子,可以看到 父组件传给子组件了一个属性和一个方法,子组件可以使用 props 中的属性和方法。对于插槽来说,父组件想访问子组件的数据,又该怎么做呢?
<!-- <Child> 组件: -->
<template>
<div>
<h1>hey,我是组件Child的标题</h1>
<slot></slot>
</div>
</template>
<script>
export default {
data() {
return{
childUser:{Name:"Tom",Age:23}
}
}
</script>
当Father使用Child组件时,想访问Child中的数据 childUser 并且将其展示在插槽的位置:
<!-- 这是父组件<Father>哦-->
<div>
<h1>hey,我是父组件Father的标题</h1>
<Child>
{{childUser.Name}}
</Child>
</div>
然而上述代码不会正常工作,因为
父级模板里的所有内容都是在父级作用域中编译的;子级模板里的所有内容都是在子作用域中编译的。
只有 <Child> 组件可以访问到 childUser,而我们提供的内容【{undefined{childUser.Name}}, {undefined{childUser.Age}}】是在父级<Father> 中渲染的。
为了让 childUser 在父级的插槽内容中可用,需要把 childUser 从 <Child>子级作用域传递到 <Father>父级作用域。
做法就是将 childUser 作为 <slot> 元素的一个属性绑定上去:
<!-- <Child> 组件: -->
<template>
<div>
<h1>hey,我是组件Child的标题</h1>
<slot v-bind:childData="childUser"></slot>
</div>
</template>
<script>
export default {
data() {
return{
childUser:{Name:"Tom",Age:23}
}
}
</script>
绑定在 <slot>
元素上的属性childData 被称为插槽 prop。
现在,在父级作用域中,我们可以使用带值的 v-slot 来定义 插槽 prop 的名字:
<!-- 这是父组件<Father>哦-->
<div>
<h1>hey,我是父组件Father的标题</h1>
<Child>
<template v-slot:default="slotProps">
{{ slotProps.childData.Name}}
{{ slotProps.childData.Age}}
</template>
</Child>
</div>
在这个例子中,我们将包含 [ 所有插槽 prop 的对象 ] 命名为 slotProps,也可以自定义。
因为在上述情况下(这里就是上面说的那一种例外情况),被提供的内容只有默认插槽,组件的标签可以被当作插槽的模板来使用。这样我们就可以把 v-slot 直接用在组件上:
<!-- 这是父组件哦-->
<div>
<h1>hey,我是父组件Father的标题</h1>
<Child v-slot:default="slotProps">
{{ slotProps.childData.Name}}
{{ slotProps.childData.Age}}
</Child>
</div>
还可以省略default。就像未指明的内容对应默认插槽一样,不带参数的 v-slot 被假定对应默认插槽:
<!-- 这是父组件哦-->
<div>
<h1>hey,我是父组件Father的标题</h1>
<Child v-slot="slotProps">
{{ slotProps.childData.Name }}
{{ slotProps.childData.Age}}
</Child>
</div>
但是默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确:
<!-- 无效,会导致警告 -->
<Child v-slot="slotProps">
{{ slotProps.childData.Name }}
<template v-slot:other="otherSlotProps">
slotProps is NOT available here
</template>
</Child >
只要出现多个插槽,请始终为所有的插槽使用完整的基于 <template>
的语法:
<!-- 这是子组件哦 -->
<template>
<div>
<h1>hey,我是组件Child的标题</h1>
<slot v-bind:childData="childUser"></slot> <!--匿名插槽-->
<slot name="other" v-bind:otherChildData="otherChildUser"></slot> <!--具名插槽-->
</div>
</template>
<script>
export default {
data() {
return{
childUser: {Name:"Tom",Age:23},
otherChildUser:{Name:"Tom",Age:23}
}
}
</script>
<!-- 这是父组件哦-->
<Child >
<template v-slot:default="slotProps">
{{ slotProps.childData.Name }}
</template>
<template v-slot:other="otherSlotProps">
{{otherSlotProps.otherChildData.Name}}
</template>
</Child>
4.解构插槽(prop)
作用域插槽的内部工作原理是将你的插槽内容包括在一个传入单个参数的函数里,所以,这意味着 v-slot 的值实际上可以是任何能够作为函数定义中的参数的 JavaScript 表达式。
<Child v-slot="{childData}">
{{ childData.Name }}
</Child>
这样可以使模板更简洁,尤其是在该插槽提供了多个 prop 的时候。它同样开启了 prop 重命名等其它可能,例如将 childData重命名为 person:
<Child v-slot="{ childData: person }">
{{ person.Name }}
</Child >
你甚至可以定义默认内容,用于插槽 prop 是 undefined 的情形:
<Child v-slot="{ childData= { Name: 'Guest' } }">
{{ childData.Name }}
</Child >
这块看不明呗的请移步 ——> 变量的解构赋值—ES6入门
三、v-slot、slot-scope 和 slot
slot
和 slot-scope
已经被废弃,所有的 2.x 版本中 slot
和 slot-scope
属性仍会被支持,但已经被官方废弃且不会出现在 Vue 3 中。
所以更推荐使用vue2.6.0中的 v-slot
。
(1)v-slot 的使用
1、在一个 <template>元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称。
2、当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。这样我们就可以把 v-slot 直接用在组件标签上。(这就是上面说的那种例外情况)
3、跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把 v-slot: 替换为字符 #。
例如 v-slot:header 可以被重写为 #header,和其它指令一样,该缩写只在其有参数的时候才可用。
也就是说,如果你希望使用缩写的话,你必须始终以明确插槽名取而代之,default不可以省略:
<Child #default="{childData}">
{{ childData.Name }}
</Child >
(2)slot & slot-scope的使用
<div class="child">
<div>
<slot name="mySlot" :msg="msg"> </slot>
<p>这里是child 组件</p>
</div>
</div>
<h1>这里是父组件</h1>
<child >
<div slot="mySlot" slot-scope="childData">//作用域插槽的用法(slot-scope)
{{ childData.msg }}
</div>
</child >
1、 slot=“default” 可以省略不写,slot可以用在 <template>元素,也可以用在任意的普通元素上。
2、这里的 slot-scope 声明了被接收的 prop 对象会作为 slotProps 变量存在于 <template> 作用域中。你可以像命名 JavaScript函数参数一样随意命名 slotProps。同样的,slot-scope可以用在 template元素,也可以用在任意的普通元素上。