Vue(四):插槽

67 阅读3分钟

概念

插槽就是子组件中的提供给父组件使用的一个占位符,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的标签。

插槽显不显示、怎样显示是由父组件来控制的,而插槽在哪里显示就由子组件来进行控制。

基本使用

子组件:

<template>
    <div>
        <h1>今天天气状况:</h1>
        <slot></slot>
    </div>
</template>

<script>
export default {
    name: 'child'
}
</script>

父组件:

<template>
  <div class="box">
    <div>使用slot分发内容</div>
    <child>
      <div>多云</div>
    </child>
  </div>
</template>

<script>
import Child from './Child.vue';
export default {
  components: { Child },
};
</script>

<style scoped lang="less">
.box {
  margin: 20px;
}
</style>

在这里插入图片描述


如果把子组件中 slot 去掉,父组件不变:

<template>
    <div>
        <h1>今天天气状况:</h1>
        <!-- <slot></slot> -->
    </div>
</template>

<script>
export default {
    name: 'child'
}
</script>

在这里插入图片描述


如果子组件没有使用插槽,父组件如果需要往子组件中填充模板或者 html, 是没法做到的

具名插槽

具名插槽其实就是给插槽娶个名字。一个子组件可以放多个插槽,而且可以放在不同的地方,而父组件填充内容时,可以根据这个名字把内容填充到对应插槽中。

子组件

<template>
  <div>
    <div class="top">
      <h1>头部</h1>
      <slot name="top"></slot> <!--具名插槽-->
    </div>
    <div class="center">
      <h1>中间</h1>
      <slot></slot> <!--默认插槽-->
    </div>
    <div class="bottom">
      <h1>底部</h1>
      <slot name="bottom"></slot> <!--具名插槽-->
    </div>
  </div>
</template>

<script>
export default {
  name: "child",
};
</script>

<style scoped lang="less">
.top {
  background-color: aqua;
}
.center {
  background-color: pink;
}
.bottom {
  background-color: aquamarine;
}
</style>

父组件:

<template>
  <div class="box">
    <div>使用slot分发内容</div>
    
    <child>
    
      <!--所有没指定插槽名称的后会添加到默认插槽中-->
      <div>A</div>
      <div>
        B<span>C</span>
      </div>
      
      <!--具名插槽写法1 vue2写法-->
      <div slot="bottom">E</div>
      
      <!--具名插槽写法2 vue3写法-->
      <template v-slot:top>
        <div>D</div>
      </template>
      
    </child>
    
  </div>
</template>

<script>
import Child from "./Child.vue";
export default {
  components: { Child },
};
</script>

<style scoped lang="less">
.box {
  margin: 20px;
}
</style>

在这里插入图片描述

无论父组件中插槽的顺序是怎样的,最终渲染结果都以子组件中插槽的顺序为准。

作用域插槽

作用域插槽其实就是带数据的插槽,即带参数的插槽,简单的来说就是子组件提供给父组件的参数,该参数仅限于插槽中使用,父组件可根据子组件传过来的插槽数据来进行不同的方式展现和填充插槽内容。

子组件:

<template>
  <div>
      <h1>我是子组件</h1>
      <slot :aa="list" bb="123"></slot>
  </div>
</template>

<script>
export default {
  name: "child",
  data() {
      return {
          list: [
              1,2,3,4,5
          ]
      }
  }
};
</script>

<style scoped lang="less">
</style>

父组件:

<template>
  <div class="box">
    <div>我是父组件</div>

    <child>
      <div slot-scope="ss1">
        <!--ss1这个名字是随便取的-->
        {{ ss1 }}
        <div>呵呵</div>
      </div>
    </child>

    <hr />

    <child>
      <div slot-scope="{ bb }"> <!--括号中的属性名必须是子组件中存在的-->
        {{ bb }}
        <div>嘻嘻</div>
      </div>
    </child>

    <hr />

    <child>
      <ul slot-scope="ss2">
        <li v-for="(item, index) in ss2.aa" :key="index">{{ item }}</li>
      </ul>
    </child>

    <hr />

    <child> 哈哈 </child>
  </div>
</template>

<script>
import Child from "./Child.vue";
export default {
  components: { Child },
};
</script>

<style scoped lang="less">
.box {
  margin: 20px;
}
</style>

在这里插入图片描述

常用场景(以下为常用的情况之一): 如果子组件中的某一部分的数据,每个父组件都会有自己的一套对该数据的不同的呈现方式,这时就需要用到作用域插槽。

vue2 和 vue3 写法的区别

基本使用每区别

具名插槽

vue2:

<!--子组件-->
<slot name="top"></slot>

<!--父组件-->
<div slot="top">xxx</div>

vue3:

<!--子组件-->
<slot></slot>
<slot name="top"></slot>

<!--父组件-->
<template v-slot>
  <div>xxx</div>
</template>

<template #default>
  <div>xxx</div>
</template>

<template v-slot:top>
  <div>xxx</div>
</template>

<template #top>
  <div>xxx</div>
</template>

作用域插槽

vue2:

<!--子组件-->
<slot :aa="list" bb="123"></slot>
// list: [ 1, 2, 3, 4, 5 ]

<!--父组件-->
<div slot-scope="ss1">
  {{ ss1 }} //输出 { "aa": [ 1, 2, 3, 4, 5 ], "bb": "123" }
</div>

<div slot-scope="{ bb }"> <!--括号中的属性名必须是子组件中存在的-->
  {{ bb }}  // 输出 123
</div>

vue3:

<!--子组件-->
<slot :aa="list" bb="123" name="top"></slot>

<!--父组件-->
<template v-slot:top="ss1">
  <div>{{ ss1 }}</div> //输出 { "aa": [ 1, 2, 3, 4, 5 ], "bb": "123" }
</template>

<template v-slot:top="{ bb }">
  <div>{{ bb }}</div> // 输出 123
</template>

vue3 写法扩展

<!--子组件-->
<template>
  <div>
      <h1>我是子组件</h1>
      <slot :aa="list" bb="123"></slot>
      <slot name="top"></slot>
  </div>
</template>

<script>
export default {
  name: "child",
  data() {
      return {
          list: [
              1,2,3,4,5
          ]
      }
  }
};
</script>

<style scoped lang="less">
</style>
<!--父组件-->
<template>
  <div class="box">
    <div>我是父组件</div>

    <child>
      <template v-slot="ss1">
        <div>传递所有参数:{{ ss1 }}</div>
      </template>
    </child>

    <child>
      <template v-slot="{ bb }">
        <div>通过解构传递具体参数:{{ bb }}</div>
      </template>
    </child>

    <child>
      <template v-slot="{ bb: haha }">
        <div>参数重命名:{{ haha }}</div>
      </template>
    </child>

    <child>
      <template v-slot="{ cc = '默认值' }">
        <div>参数默认值:{{ cc }}</div>
      </template>
    </child>

    <child>
      <template v-slot:[slotName]="{ dd = '默认值' }">
        <div>动态插槽名和默认值:{{ dd }}</div>
      </template>
    </child>
  </div>
</template>

<script>
import Child from "./Child.vue";
export default {
  components: { Child },
  data() {
    return {
      slotName: "top",
    };
  },
};
</script>

<style scoped lang="less">
.box {
  margin: 20px;
}
</style>