相关文章推荐
想出国的大象  ·  vue ...·  1 月前    · 
无聊的莴苣  ·  Vue 中 $set() 与 ...·  4 周前    · 
坚韧的跑步鞋  ·  Layui ...·  4 周前    · 
挂过科的镜子  ·  VUE 使用 select ...·  4 周前    · 

个人对插槽的理解

插槽是子组件提供给父组件修改自身某个位置的内容、或在某个位置添加内容的一种手段。形象来讲就是组件定义时会在指定位置留下一些坑位,父组件调用时可以按需往坑里面填充一些内容或修改默认内容。

插槽分为匿名插槽和具名插槽两种类型,实际上匿名插槽也是有名字的,匿名插槽的名字是 default

匿名插槽使用示例

子组件是一个按钮组件,通过内置组件 slot 指定插槽位置,用于展示按钮文字描述。

<template>
    <button class="my-button" v-on="$listeners" v-bind="$attrs">
        <slot></slot>
    </button>
</template>
<script>
export default {
    name: "myButton",
</script>
<style lang="css">
.my-button {
    outline: none;
    color: rgb(206, 17, 17);
    background: #fff;
    border: 1px solid rgb(206, 17, 17);
    padding: 4px 8px;
    font-size: 15px;
    border-radius: 4px;
    cursor: pointer;
.my-button:disabled {
    color: #999;
    border: 1px solid #999;
    cursor: not-allowed;
</style>

父组件调用时直接在组件标签内填充按钮文字内容即可,用法和原生按钮保持一致。

小提示: 插槽填充内容不限制一定是文字,也可以是其他html元素或者vue组件(不限个数),只是本例中的需求是填充文字而已。

<template>
    <div class="app-container">
        <button @click="handleClick">原生按钮</button>
        <button disabled>原生禁用按钮</button>
        <my-button @click="handleClick">好看的按钮</my-button>
        <my-button disabled>好看的禁用按钮</my-button>
        <my-button>
            <div>可以填充一个div</div>
        </my-button>
    </div>
</template>
<script>
import myButton from "./button.vue"
export default {
    name: "App",
    components: {
        myButton
    methods: {
        handleClick(e) {
            console.log(e)
</script>
<style lang="css">
button+button {
    margin-left: 10px;
</style>

效果展示:

具名插槽使用示例

子组件是一个卡片组件,卡片分为头部和内容两部分,头部使用具名插槽,内容使用匿名插槽,具名插槽通过name属性指定插槽名称。 在这个组件中,由于slot组件无法添加class属性,我们为了预设定头部的样式,将头部插槽包裹在一个div里面了,但是如果在父组件没使用头部插槽时,我们希望子组件隐藏包裹插槽的div,所以这里使用vue实例属性$slots加了一个判断,这样就可以达到隐藏包裹元素的作用。

<template>
    <div class="my-card">
        <!-- 使用$slots[插槽名]判断父组件有没有使用该插槽,如果没有使用则不显示 -->
        <div class="header" v-if="$slots.header">
            <slot name="header"></slot>
        </div>
        <div class="content">
            <slot></slot>
        </div>
    </div>
</template>
<script>
export default {
    name: "myCard",
</script>
<style lang="css">
.my-card {
    margin: 10px;
    border: 1px solid #eee;
    box-shadow: 0 0 15px 3px rgba(0, 0, 0, .3);
.my-card .header {
    border-bottom: 1px solid #e8e8e8;
    padding: 8px 10px 8px 20px;
    font-weight: bold;
    position: relative;
.my-card .header::before {
    content: "";
    width: 4px;
    height: 16px;
    position: absolute;
    left: 8px;
    top: 50%;
    margin-top: -8px;
    background-color: #fd2323;
.my-card .content {
    padding: 10px;
</style>

父组件使用子组件插槽有两种写法,推荐使用新语法

旧语法是使用属性来指定插槽位置:slot="插槽名称"

新语法(v2.6以上)是使用指令来指定插槽位置:v-slot:插槽名称,而且v-slot指令只能作用于template组件上

<template>
    <div class="app-container">
        <my-card>
            <!-- 旧语法 -->
            <div slot="header">使用slot属性指定要插到头部插槽位置</div>
            <div>我是内容,不需要指定插入位置</div>
        </my-card>
        <my-card>
            <!-- 新语法 -->
            <template v-slot:header>
                <div>使用slot属性指定要插到头部插槽位置</div>
            </template>
            <div>我是内容,不需要指定插入位置</div>
        </my-card>
        <my-card>
            <div>我是内容,这个卡片不显示头部</div>
        </my-card>
    </div>
</template>
<script>
import myCard from "./card.vue"
export default {
    name: "App",
    components: {
        myCard
</script>

效果展示:

具名插槽的name属性也可以是动态绑定的,动态的name属性在配合v-for使用时可以搭配出很灵活的写法。

默认显示内容

在父组件没有传入插槽内容时,我们可以在子组件的插槽位置显示一个默认的内容,例如上文的按钮组件,我们可以在slot标签中间加入提交两个字,父组件调用时不传文字,那么这个按钮的文字就显示为提交。 子组件代码

<slot>提交</slot>

父组件代码

<my-button></my-button>

显示效果:

该功能在具名插槽上用法一致。

插槽传值(作用域插槽)

slot组件跟普通组件一样可以使用v-bind指令绑定prop属性,父组件可以通过slot-scope属性(旧语法)或者v-slot指令(新语法,v2.6+)拿到这些值。 子组件是一个单纯展示加法的组件

<template>
    <div class="my-add">
        <span v-for="(num, i) in nums" :key="i">
            {{ num }}
            <span class="add-symbol" v-if="i < nums.length - 1">+</span>
        </span>
        <span class="equal-symbol">=&nbsp;</span>
        <slot :result="result">{{ result }}</slot>
    </div>
</template>
<script>
export default {
    name: "myAdd",
    props: {
        nums: {
            type: Array,
            default: () => [],
    computed: {
        result() {
            return this.nums.reduce((a, b) => a + b);
</script>

父组件希望拓展子组件的功能,在加法结果大于100时显示红色告警颜色。

<template>
    <div class="app-container">
            <my-add v-for="(item, i) in list" :nums="item" :key="i">
                <!-- 旧语法 -->
                <span slot-scope="{result}" :class="result > 100 ?'danger':'' ">{{result}}</span>
            </my-add>
        </div>
            <my-add v-for="(item, i) in list" :nums="item" :key="i">
                <!-- 新语法 -->
                <template v-slot:default="{result}">
                    <span :class="result > 100 ?'danger':'' ">{{result}}</span>
                </template>
            </my-add>
        </div>
    </div>
</template>
<script>
import myAdd from "./add.vue";
export default {
    name: "App",
    components: {
        myAdd,
    data() {
        return {
            list: [
                [1, 2, 3, 4, 5, 6],
                [10, 20, 30, 40, 50, 60],
</script>
<style lang="css">
.danger {
    color: #f00;
</style>

显示效果:

如果传值的是具名插槽,则新语法表示为v-slot:插槽名称="slotProps",其中slotProps可以解构为具体的prop属性。

在render函数中使用插槽

还是以上文中的按钮组件为例,转换为render函数的写法如下

render() {
  return <button class="my-button2" on={this.$listeners} {...this.$attrs}>
      {this.$slots.default}
  </button>;

如果是函数式组件,应该这样写

render(_, {slots, listeners, data}) {
    return <button class="my-button2" on={listeners} {...data}>
        {slots().default}
    </button>;