一、背景
假设两个相似的页面,他们只在某一模块有不同的UI效果,可能会想到v-if
,v-else-if
,v-else
,导致代码不易维护,此时slot就起到了作用,可以在组件中使用<slot></slot>
占坑,其可以传任何东西,包括HTML等。
日常项目中,可以将一个业务组件剥离出公用部分成为通用组件,通过slot再将所需要的业务内容插入对应插槽中,在父组件中调用业务组件。
通过插槽我们不难
Q&A
插槽和prop数据同为父子组件之间的传值,有什么区别呢?
父组件通过props向子组件传递数据,通过插槽向子组件传递内容,其中prop传递的是组件的属性,插槽传递的VNode节点。
二、分类及使用方法
1. 基础插槽
//parent.vue
<child>
hello {{word}} //这里所有的内容都会被渲染到slot标签位置
</child>
//child.vue
<a href="#">
<slot></slot>
</a>
parent组件中的child内可以访问父组件的所有数据,比如word,作用域为parent的作用域,不可以访问child的数据。
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
2. 带默认内容的插槽
//parent.vue
<child>
</child>
//child.vue
<a href="#">
<slot>默认内容</slot>
</a>
当parent中child没有内容时,就会渲染「默认内容」,反之,就渲染parent中child内容。
3. 具名插槽
(在之后的版本里尽量都用v-slot
指令。)
//parent.vue
<child>
<template v-slot:header></template>
<template #footer></template> //缩写形式
</child>
//child.vue
<a>
<slot name="header"></slot>
<slot name="footer"></slot>
</a>
如果没写名字插槽,那么默认name
为default
默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确
4. 作用域插槽
作用域插槽就是在父组件中书写的插槽内容可以访问使用子组件的数据。
内部工作原理:将你的插槽内容包括在一个传入单个参数的函数里。
子组件<slot>
元素上绑定需要传递到父组件的值(被称为插槽 prop),并在父组件中通过v-solt:default=“xxx”(简写:v-solt=“xxx”),来接受子组件传过来的数据并通过xxx.来访问。
(在之后的版本里尽量都用v-slot
指令。)
//parent.vue
<child>
<template v-slot:"scop">{{scop.people.name}}</template>
</child>
//child.vue
<a>
<slot v-bind:people="people"></slot>
</a>
三、实际项目中使用
1. 日常使用将「业务组件」剥离成「通用组件」
<template>
<ul slot="content" v-if="Lists.length">
<li v-for="item in Lists" :key="item.id">
<div class="l">
<slot name="left" :item="item">图片区域</slot>
</div>
<div class="r">
<slot name="right" :item="item">详情区域</slot>
</div>
</li>
<slot name="after"></slot>
</ul>
</template>
2. 对于想传入多个组件
//parent.vue
<child>
<template #index>
<li>li1</li>
</template>
<template #index>
<li>li2</li>
</template>
<template #index>
<li>li3</li>
</template>
</child>
//child.vue
<ul>
<template v-for="(cpt, index) in cpts">
<component :is="cpt"></component>
</template>
</ul>
<script>
export default {
created() {
this.cpts = this.$slots.index.map((slot, index) => {
return Vue.component(`middle-slot-${index}`, {
render: () => slot
});
});
}
}
</script>