solt 插槽需要分为 2.6.0 版本以上和 2.6.0版本以下。
2.6.0 版本以下的 slot 插槽在,2.x版本将继续支持,但是在 Vue 3 中已被废弃,且不会出现在官方文档中。
作用
插槽 prop 允许我们将插槽转换为可复用的模板,这些模板可以基于输入的 prop ( 也就是在使用者中传递的内容 ) 渲染出不同的内容。这在设计封装数据逻辑同时允许父级组件自定义部分布局的可复用组件时是最有用的。
2.6.0 版本及以上
在 2.6.0 中,Vue 为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot
指令)。它取代了 这两个目前已被废弃但未被移除且仍在文档中的 attribute。slot
和 slot-scope
默认插槽: 当组件中未提供任何插槽内容时,展示默认的内容。如提供了内容,则替代默认内容 ( 只管挖坑,不管怎么用,反正我都丢进去)
Category 组件:分类组件,插槽中提供默认内容
<template>
<div>
<p>游戏列表</p>
<slot>当没有外部内容填充时,我会展示</slot>
</div>
</template>
App 组件:使用 Category 组件,且不提供插槽内容。
<template>
<div id="app">
<Category/>
</div>
</template>
只展示默认内容。
当 App 组件中提供插槽内容时:Category 组件就不能使用自闭合,需要使用双标签形式。此时 Vue 会将 组件标签中 的内容在 App 组件中解析完成之后,丢到 Category 组件 中,将 slot 替换
<template>
<div id="app">
<Category>
<p>这是 App 组件提供的内容</p>
</Category>
</div>
</template>
提供的内容会覆盖默认内容
具名插槽:顾名思义就是有个名字的插槽( 给这个坑取了个名字,使用的时候可以指定往这个坑里面填东西 ) --- v-slot:xxx 只能存在于 <template> 标签中 ( ps : 存在一种情况可以直接写在组件标签上)
Category 组件:分类组件,插槽中提供 name 属性,作为该插槽名称。若是不提供 name 属性,则默认带有隐含的名字“default”,作为默认插槽
<template>
<div>
<p>游戏列表</p>
<slot name="header">这是头部</slot> <br>
<slot>这是内容</slot> <br>
</div>
</template>
App组件:通过 v-slot 指令,绑定对应的插槽名称 ( name 属性),就能将内容分发到指定插槽中。
<template>
<div id="app">
<Category>
<template v-slot:header>
<p>传递的头部</p>
</template>
<template>
<p>传递的内容</p>
</template>
</Category>
</div>
</template>
但是,如果 Category 组件中存在多个 slot 插槽,任何没有被包裹在带有 v-slot
的 <template>
中的内容都会被视为默认插槽的内容。
具名插槽也可以和 v-bind 、v-on 一样进行缩写,v-slot: 缩写成 #。如下所示。但是切记一点:如果你想使用缩写,那么必须给一个明确的插槽name,即使是 default 也要写上。要么就不写#,直接为默认插槽,否则会报错
// 全写
<Category>
<template v-slot:gameList>xxx</template>
</Category>
// 简写
<Category>
<template #gameList>xxx</template>
</Category>
// 默认插槽简写完整
<Category>
<template #default>xxx</template>
</Category>
// 默认插槽简写不完整报错
<Category>
<template #>xxx</template>
</Category>
作用域插槽:在使用组件时,让使用者中提供的插槽内容能够访问子组件中才有的数据。父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
Category 组件:分类组件,组件中带有游戏分类数据。
<template>
<div>
<p>游戏列表</p>
<slot></slot>
</div>
</template>
<script>
export default {
data() {
return {
games:['lol','dota','刺客信条','只狼']
}
},
}
</script>
App 组件:使用 Category 组件中的数据渲染出不同的效果、分别渲染为 有序列表,无序列表、h4
<template>
<div id="app">
<Category>
<ul>
<li v-for="(game,index) in games" :key="index">
<p>{{ game }}</p>
</li>
</ul>
</Category>
</div>
</template>
代码写的很流畅,看起来完全没毛病,但是控制台报错了啊。
翻译过来就是:属性或方法“games”不是在实例中定义的,而是在渲染过程中引用的。通过初始化属性,确保该属性是可响应的,无论是在data选项中,还是对于基于类的组件。
这说明了啥?在 App 组件中报了这个错,games 不存在,肯定不存在啊,我的 games 数据是存在 <Category> 组件中的啊,在 <Category> 组件中随便你怎么使用都没毛病。但是 App 组件自己没定义,还要强行使用,那咋行。
所以 Vue 使用了 v-bind 指令( 简写为 : ),让 使用者App组件 能够访问到被使用者 <Category> 组件中的数据
<Category> 组件中,通过 v-bind 指令给 slot 插槽绑定了 games 属性,传递的数据就是 games。从此, <Category> 组件 的使用者,只要接收 games 数据,就可以使用。
<template>
<div>
<p>游戏列表</p>
<slot v-bind:games="games"></slot>
<!-- <slot :games="games"></slot> -->
</div>
</template>
App组件,通过使用 <Category> 组件,且在 <template> 标签中 通过 v-slot 指令获取数据即可使用。( 这里的 :default 对应上面的,当 slot 插槽未指定 name 属性时,默认自带 default ,可以省略。如果是具名插槽,则指定 对应的 name 属性进行渲染 )
<template>
<div id="app">
<Category>
<template v-slot:default='slotProps'>
<!-- <template v-slot='slotProps'> -->
<ul>
<li v-for="(game, index) in slotProps.games" :key="index">
<p>{{ game }}</p>
</li>
</ul>
</template>
</Category>
</div>
</template>
<template> 标签中 通过 v-slot='slotProps' 接收了这个 slotProps,这个名字是随便定义的,对应的就是 <Category> 组件中 <slot> 中绑定的 games 数据。slotProps 是一个对象,键值对就是绑定的 { games:['lol','dota','刺客信条','只狼'] }。所以在使用中,还需要通过 slotProps.games 。
当然,也存在简写方式,那就是 es6 的解构。可以将 slotProps 中的值解构出来直接使用。也可以 重命名,添加默认值等 解构赋值的常用操作
// 单纯解构
<Category>
<template v-slot:default={games}>
<ul>
<li v-for="(game, index) in games" :key="index">
<p>{{ game }}</p>
</li>
</ul>
</template>
</Category>
// 解构 + 重命名 将 games 重命名未 aaa
<Category>
<template v-slot={games:aaa}>
xxx
</template>
</Category>
// 解构 + 设置默认值
<Category>
<template v-slot={games:['恐龙快打']}>
xxx
</template>
</Category>
动态插槽名:当你的插槽名称不固定,而是根据情况而变化时,你就需要使你的插槽名称是动态的
在 官方文档--模板语法--指令中,存在一个动态参数,具体如下,限制内容请查看文档。
// 这里的 attributeName 会被作为一个 JavaScript 表达式进行动态求值,求得的值将
// 会作为最终的参数来使用。例如,如果你的 Vue 实例有一个 attributeName,其值为 "href",
// 那么这个绑定将等价于 v-bind:href。
<a v-bind:[attributeName]="url"> ... </a>
所以 <slot> 插槽也借鉴了这个方法,实现了 动态插槽名
<Category>
<template v-slot:[这是动态插槽名称]>
xxx
</template>
</Category>
独占默认插槽:在传递数据的前提下,如果被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。之前说过 v-slot 必须写在 <template> 标签中,只有一种情况除外,说的就是这个情况。
如果我的 <Category> 组件 中只存在一个默认插槽。且传递了数据
<template>
<div>
<p>游戏列表</p>
<slot :games="games"></slot>
</div>
</template>
那么这个默认插槽在被使用时,v-slot 指令,可以直接作用在 <Category> 组件标签上,可以简写为 <Category v-slot={games}>
<template>
<div id="app">
<Category v-slot:default={games}>
{{ games }}
</Category>
</div>
</template>
这里说一下为啥需要这个 games:如果不接受参数,那是不是就直接是默认插槽,都默认插槽了,还写个锤子的 v-slot 指令啊,直接怼代码不就完事了。
2.6.0版本以下语法
事先声明,在接下来所有的 2.x 版本中 slot
和 slot-scope
属性仍会被支持,但已经被官方废弃且不会出现在 Vue 3 中。
slot:类比于上面的 v-solt,作用于具名插槽与默认插槽,与 v-solt 不同的是,solt 可以写在 <template> 标签中,也可以使用在一个普通元素上。不存在简写方式。
<Category> 组件中存在三个插槽,一个是默认插槽,两个是具名插槽
<template>
<div>
<p>游戏列表</p>
<slot name="header"></slot>
<slot></slot>
<slot name="footer"></slot>
</div>
</template>
App 组件中,分别用 <template> 标签 + slot 指定具名插槽 header,普通元素 p 标签 + slot 指定具名插槽 footer,纯元素的默认插槽,来实现效果
<template>
<div id="app">
<Category>
<template slot='header'>这是头部</template>
<p>这是内容</p>
<p slot="footer">这是底部</p>
</Category>
</div>
</template>
展示结果如下,具名插槽一一对应,默认插槽内容页分发到了正确位置。
slot-scope:作用域插槽,同样可以写在 <template> 标签中,也可以使用在一个普通元素上。用法和新版本的 v-slot 作用域插槽一致,可以解构,只是语法不一致
<Category> 组件中存在只存在一个具名插槽,且通过 v-bind: 绑定数据
<template>
<div>
<p>游戏列表</p>
<slot name="header" v-bind="games"></slot>
</div>
</template>
App 组件中, 通过 slot-scope 接收参数,通过 slot 指定具名插槽 进行渲染。
<Category>
<template slot='header' slot-scope="games">{{ games }}</template>
</Category>
通过作用域插槽,可以实现 在复用模板的情况下,渲染出不同的 布局和样式。因为 作用域插槽的渲染实际上是受 父级使用者控制的。将数据传递给 使用者后,使用者可以在自己主见内部随意使用,任意布局。
<Category> 组件中代码不变
<template>
<div>
<p>游戏列表</p>
<slot name="header" v-bind="games"></slot>
</div>
</template>
App 组件中 使用 <Category> 组件 进行不同的布局
<template>
<div id="app">
<Category>
<template slot='header' slot-scope="games">
<ul>
<li v-for="(item,index) in games" :key="index">{{ item }}</li>
</ul>
</template>
</Category>
<Category>
<template slot='header' slot-scope="games">
<span style="color:red" v-for="(item,index) in games" :key="index">{{ item }}</span>
</template>
</Category>
</div>
</template>
这就是插槽的便利之处