目录
Vue 实现了一套内容分发的 API,将
<slot>
元素作为承载分发内容的出口。简单来讲:默认情况下我们在父组件中加入到子组件的DOM是不会显示的,但是如果我们需要插入一段DOM,那么这段DOM是否显示,如何显示,显示在什么位置,就需要使用slot内容分发。
一、默认情况下(不使用slot)
正常情况下我们在引用子组件时另加的标签是不会展示的,举例说明:
<!-- 这是父组件 father.vue 的内容 -->
<template>
<div class='app-container'>
<son>
<p>这是父组件想要放到子组件里面的内容</p>
</son>
</div>
</template>
<script>
import son from './son';
export default {
components: {
son
},
};
</script>
<!-- 这是子组件 son.vue 的内容 -->
<template>
<div class='app-container'>
<p>这是一个子组件--start--</p>
<p>这是一个子组件--end--</p>
</div>
</template>
虽然我们在<son></son>标签内部加入了一段HTML代码,但是在渲染的过程中,这段代码是不会显示的。渲染结果如下:
怎么才能显示出父组件想要放在子组件的内容呢?这就需要用到slot内容分发。
二、单个slot
如果我们在子组件内部添加了一个<slot></slot>标签做一个占位,就可以将父组件想要放在子组件的内容,放到想让他显示的地方(即slot标签所在的地方);也就是说:父组件放在子组件里的内容,插入到了子组件的<slot></slot>位置。
注意,即使有多个标签,也会一起被插入,相当于用父组件放在子组件里的所有标签,替换了<slot></slot>这个标签。
如:在上述示例的 son.vue 文件中加入slot,具体如下:
此时的渲染结果就变成了如下的结果,成功的将父组件插入在子组件内的DOM显示了出来。
三、具名slot
给<slot>元素指定一个name后可以分发多个内容,具名slot可以和单个slot共存。
<!-- 这是父组件 father.vue 的内容 -->
<template>
<div class='app-container'>
<son>
<p slot="header">这是父组件想要插入到具名slot:header的内容</p>
<p slot="footer">这是父组件想要插入到具名slot:footer的内容</p>
</son>
</div>
</template>
<script>
import son from './son';
export default {
components: {
son
},
};
</script>
<!-- 这是子组件 son.vue 的内容 -->
<template>
<div class='app-container'>
<p>这是一个子组件--start--</p>
<slot name="header"></slot>
<p>这是子组件本身的内容</p>
<slot name="footer"></slot>
<p>这是一个子组件--end--</p>
</div>
</template>
渲染结果如下,多个标签分别渲染到了其对应的具名slot指向的位置,而父组件中没有指定 slot="xxx" 的标签会被统一渲染到不单个<slot></slot>位置:
四、编译作用域
父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译。
<p slot="header"></p>受父组件的控制,如果想要调用某个方法,则该方法需要被定义在父组件的methods当中。
五、默认插槽的内容
很多时候,我们可以为 slot 设置默认内容,当父组件中有内容传入的时候,会替换该默认内容;若是父组件中不传入任何内容,则默认内容显示。
<!-- 这是父组件 father.vue 的内容 -->
<template>
<div class='app-container'>
<son>
<!-- 父组件什么都不传入 -->
</son>
</div>
</template>
<script>
import son from './son';
export default {
components: {
son
},
};
</script>
<!-- 这是子组件 son.vue 的内容 -->
<template>
<div class='app-container'>
<p>这是一个子组件--start--</p>
<button>
<slot>默认为submit按钮</slot>
</button>
<p>这是一个子组件--end--</p>
</div>
</template>
结果如下:
若有传入内容,则会替换掉默认内容:
六、作用域插槽
作用域插槽是一个特殊的slot,使用一个可以复用的模板替换已渲染的元素。
6.1 具体用法:
(1)在父组件中传入由一个 <template></template> 标签包裹的DOM元素,并且这个 template 标签拥有 slot-scope="props" 属性;(props 相当于一个临时变量,有点类似于 v-for 里面的 item,一般叫props,可以换成任何名称)
(2)通过props就可以访问来自子组件slot的数据;
<!-- 这是父组件 father.vue 的内容 -->
<template>
<div class='app-container'>
<son>
<template slot-scope="props">
<p>父组件传入的内容</p>
<p>{{props.msg}}</p>
</template>
</son>
</div>
</template>
<script>
import son from './son';
export default {
components: {
son
},
};
</script>
<!-- 这是子组件 son.vue 的内容 -->
<template>
<div class='app-container'>
<p>这是一个子组件--start--</p>
<slot msg="子组件传过来的msg"></slot>
<p>这是一个子组件--end--</p>
</div>
</template>
结果如下:
在 2.5.0+,slot-scope
不再限制在 <template>
元素上使用,而可以用在插槽内的任何元素或组件上。
七、访问slot
在子组件中可以通过 this.$slots 来获取被 slot 访问的内容(貌似没有用到过这个==)
<!-- 子组件 son.vue -->
<template>
<div class='app-container'>
<p>这是一个子组件--start--</p>
<slot name="header">123</slot>
<slot name="footer">123</slot>
<slot></slot>
<p>这是一个子组件--end--</p>
</div>
</template>
<script>
export default {
mounted() {
console.log(this.$slots); // Object 所有的slot
console.log(this.$slots.header); // 获取name=header的slot
console.log(this.$slots.footer);
console.log(this.$slots.default); // 获取所有具名slot之外的slot
}
};
</script>