想象一个相框
<!-- 相框 photo-frame.vue -->
<div class="photo-frame">
<!-- 这是一个"空",等待被填入内容 -->
<slot></slot>
</div>
现在放入了一张山川的照片
<photo-frame>
<!-- 放入一张山的照片 -->
<img src="hill.jpg">
</photo-frame>
现在放入了一张河流的照片
<photo-frame>
<!-- 放入一张山的照片 -->
<img src="river.jpg">
</photo-frame>
Vue的插槽就像相框,你无需为了放新的照片而重新设计相框,可以直接复用。
现在书架上有两个相框了
我把左边的相框叫 left
,把右边的相框叫 right
<!-- Bookshelf.vue -->
<div class="bookshelf">
<!-- 左边相框位置 -->
<div class="left">
<slot name="left"></slot>
</div>
<!-- 右边相框位置 -->
<div class="right">
<slot name="right"></slot>
</div>
</div>
只有左边放山的照片,右边不放
<bookshelf>
<!-- 只在左边相框放照片 -->
<template #left>
<img src="hill.jpg" alt="山水风景">
</template>
<!-- 右边相框空着 -->
</bookshelf>
我把山的照片放左边,把河流的照片放右边
<bookshelf>
<!-- 左边相框放风景照 -->
<template #left>
<img src="landscape.jpg" alt="山水风景">
</template>
<!-- 右边相框放人物照 -->
<template #right>
<img src="portrait.jpg" alt="人物照片">
</template>
</bookshelf>
这就是Vue3 的具名插槽,一个子组件里预留两个位置,给父组件传入元素。
现在相框升级成智能相框了
可以显示照片的名字和拍摄日期
<!-- 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>
在左侧放入山的照片和数据,在右侧放入河流的照片和数据
<!-- 父组件 -->
<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>