在 vue 中,组件化是极其重要的特征,我们使用不同的组件来实现页面。但是也会经常需要将父组件的内容分发到子组件,这就需要用到slot(插槽);
// 父组件
<father>this is slot content</father>
// 子组件
<a>
<slot></slot>
</a>
这样父组件中的内容就会默认插入到slot标签中。
1.slot的默认值
我们可以设置slot 的默认值。当父级组件中使用这个组件不提供任何插槽内容时,子组件便会显示slot的默认值
// 定义一个带默认solt的组件<submit-button>
<button type="submit">
<slot>Submit</slot>
</button>
// 当我们使用这个组件并且不提供和任何插槽内容时
// 父组件
<submit-button></submit-button>
// 默认内容就会被渲染
<button type="submit">
Submit
</button>
// 当我们提供插槽内容时
// 父组件
<submit-button>
Save
</submit-button>
// 插槽的默认内容会被替换
<button type="submit">
Save
</button>
2.slot 的 name(具名插槽)
当我们需要多个 slot 时,子级组件的 slot 标签中有 name 属性定义了该插槽的名字,我们可以在父级组件中通过 v-slot(简写为#)来定义该 slot 内容或插入到哪个 slot 标签中(或者通过 slot 属性来标明该内容应该被插入到哪里,该语法自2.6.0之后被废弃)。自己组件中没有定义 name 属性的 slot 标签的 name 值默认为 default 。
// 父级组件,可以通过 template 标签来包裹代码块
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
// 子级组件<base-layout>;通过 name 属性定义 slot 标签的名字
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
3.父级组件获取子级组件的值(作用域插槽)
如果我们想在父级组件里面获取子级组件里面的值,就得用到作用域插槽
1.最初级用法
// 子级组件<current-user>
<span>
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
</span>
// 父级组件
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
- 我们在子级组件的 slot 标签中通过 v-bind(简写为:)绑定了子级组件中的 user ,传递给父级组件;
- 在父级组件中,通过 v-slot 指令获取到子级组件里绑定的 user ;
- 父级组件中 v-slot 指令只能用在 component 的根标签或者是将插槽的所有内容应 tamplate 标签包裹后放在 tamplate 标签中;
- 父级组件中 v-slot 指令的 arg(参数,上面的例子中的就是 default ),和子级组件中的 slot 名称意义对应,上面的例子中子级组件的 slot 默认是 default ,所以子级组件中的 v-slot 的参数也是 default ;
- 当 arg 不是 default 时,可以用#来简写 v-slot ;
- 父级组件中的 v-slot 指令的 value(上面例子中的 slotProps )就是包含所有插槽 prop 的对象,可以通过该对象拿到你想传递过来的子级组件的值;
2.升级用法
// 子组件
<span>
<slot :user="user" name="foo"></slot>
<slot :user="user" name="boo"></slot>
</span>
// 父组件
<current-user>
<template #foo="slotProps">
{{ slotProps.user.firstName }}
</template>
<template #boo="slotProps">
{{ slotProps.user.lastName }}
</template>
</current-user>
- 子级组件中,我们通过 name 属性命名不同的 slot 标签;
- 父级组件中,通过 v-slot(上面例子中用#简写)的 arg 来指定不同的 name ,从而指定该 slot 的内容在子级组件的位置;
3.注意事项(默认插槽的写法)
- 只有当父级组件提供的内容只有默认插槽时,才可以将v-slot指令直接用在组件的标签上
<current-user v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</current-user>
// 上面的default可以省略,就是默认插槽的写法
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
</current-user>
- 默认插槽的写法不能和具名插槽混用,这回导致作用域不明确
<!-- 无效,会导致警告 -->
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
<template v-slot:other="otherSlotProps">
slotProps is NOT available here
</template>
</current-user>
- 出现多个插槽时,请始终为所有的插槽用 tampalte 标签包裹;
<current-user>
<template #foo="slotProps">
{{ slotProps.user.firstName }}
</template>
<template #boo="slotProps">
{{ slotProps.user.lastName }}
</template>
</current-user>
4.作用域插槽的解构
<current-user>
<template v-slot="{ user }">
{{ user.firstName }}
</template>
</current-user>
5.具名插槽的缩写
- "v-slot:" 可以被简写为 "#"
// v-slot:header 可以被重写为 #header
<base-layout>
<template #header>
<h1>Here might be a page title</h1>
</template>
<template #footer>
<p>Here's some contact info</p>
</template>
</base-layout>
-
和其他指令一样,缩写必须是得有参数的时候才可用。以下的写法无效
<!-- 这样会触发一个警告 -->
<current-user #="{ user }">
{{ user.firstName }}
</current-user>
<!-- 有效 -->
<current-user #default="{ user }">
{{ user.firstName }}
</current-user>