Vue3 Slot 插槽-大白话版-微信好友排名展示和企业微信好友展示组件的原理

303 阅读2分钟

想象一个相框

image.png

<!-- 相框 photo-frame.vue -->
<div class="photo-frame">
  <!-- 这是一个"空",等待被填入内容 -->
  <slot></slot>
</div>

现在放入了一张山川的照片

image.png

<photo-frame>
  <!-- 放入一张山的照片 -->
  <img src="hill.jpg">
</photo-frame>

现在放入了一张河流的照片

image.png

<photo-frame>
  <!-- 放入一张山的照片 -->
  <img src="river.jpg">
</photo-frame>

Vue的插槽就像相框,你无需为了放新的照片而重新设计相框,可以直接复用。

现在书架上有两个相框了

我把左边的相框叫 left,把右边的相框叫 right

image.png

<!-- Bookshelf.vue -->
<div class="bookshelf">
  <!-- 左边相框位置 -->
  <div class="left">
    <slot name="left"></slot>
  </div>
  
  <!-- 右边相框位置 -->
  <div class="right">
    <slot name="right"></slot>
  </div>
</div>

只有左边放山的照片,右边不放

image.png

<bookshelf>
  <!-- 只在左边相框放照片 -->
  <template #left>
    <img src="hill.jpg" alt="山水风景">
  </template>
  
  <!-- 右边相框空着 -->
</bookshelf>

我把山的照片放左边,把河流的照片放右边

image.png

<bookshelf>
  <!-- 左边相框放风景照 -->
  <template #left>
    <img src="landscape.jpg" alt="山水风景">
  </template>
  
  <!-- 右边相框放人物照 -->
  <template #right>
    <img src="portrait.jpg" alt="人物照片">
  </template>
</bookshelf>

这就是Vue3 的具名插槽,一个子组件里预留两个位置,给父组件传入元素。

现在相框升级成智能相框了

可以显示照片的名字和拍摄日期

image.png

<!-- Bookshelf.vue (子组件) -->
<template>
  <div class="bookshelf">
    <div class="left-frame">
      <slot name="left"></slot>
    </div>
    
    <div class="right-frame">
      <slot name="right"></slot>
    </div>
  </div>
</template>

在左侧放入山的照片和数据,在右侧放入河流的照片和数据

image.png

<!-- 父组件 -->
<template>
  <div>
    <!-- 可以是响应式数据 -->
    <bookshelf>
      <template #left>
        <img :src="leftPhoto.url">
        <div class="info">
          照片名称:{{ leftPhoto.name }}
          拍摄时间:{{ leftPhoto.date }}
        </div>
      </template>
      
      <template #right>
        <img :src="rightPhoto.url">
        <div class="info">
          照片名称:{{ rightPhoto.name }}
          拍摄时间:{{ rightPhoto.date }}
        </div>
      </template>
    </bookshelf>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const leftPhoto = ref({
  url: 'hill.jpg',
  name: '山',
  date: '2024-1-1',
})

const rightPhoto = ref({
  url: 'river.jpg',
  name: '河',
  date: '2024-2-2',
})
</script>

这就是作用域插槽,用于子组件定义好相框的样式,由外部放入照片,用于创建可以高度可复用的UI组件,如ElementUI-Plus

也可以反过来,子组件定义好照片,父组件决定相框样式,常用于加密或敏感数据提供给外部展示,比如微信小游戏展示好友排行榜,游戏开发者只能决定展示的样式,而无法获取到用户的头像、昵称和好友数据。

<!-- 父组件 -->
<template>
  <bookshelf
    :left-photo="{
      name: '山',
      date: '2024-1-1',
    }"
    :right-photo="{
      name: '河',
      date: '2024-02-12',
    }"
  >
    <template #left="{ photoInfo }">
      <img src="hill.jpg">
      <div class="info">
        照片名称:{{ photoInfo.name }}
        拍摄时间:{{ photoInfo.date }}
      </div>
    </template>
    
    <template #right="{ photoInfo }">
      <img src="river.jpg">
      <div class="info">
        照片名称:{{ photoInfo.name }}
        拍摄时间:{{ photoInfo.date }}
      </div>
    </template>
  </bookshelf>
</template>
<!-- Bookshelf.vue (子组件) -->
<template>
  <div class="bookshelf">
    <div class="left-frame">
      <slot name="left" :photo-info="leftPhoto"></slot>
    </div>
    
    <div class="right-frame">
      <slot name="right" :photo-info="rightPhoto"></slot>
    </div>
  </div>
</template>

<script setup>
defineProps({
  leftPhoto: Object,
  rightPhoto: Object
})
</script>