前端八股Vue---插槽

目录

一、什么是插槽?

[二、插槽的 4 种类型](#二、插槽的 4 种类型)

三、默认插槽

子组件(Card.vue)

父组件使用

四、具名插槽

子组件(Layout.vue)

父组件使用

五、作用域插槽

子组件(UserList.vue)

父组件使用

六、动态插槽

七、完整示例:弹窗组件

八、插槽对比总结

九、面试高频问题

Q:插槽是什么?有什么作用?

Q:具名插槽和作用域插槽的区别?

[Q:v-slot 的简写是什么?](#Q:v-slot 的简写是什么?)

Q:使用插槽时,子组件和父组件都要用template包裹吗?

Q:插槽可以有默认内容吗?

Q:默认插槽和具名插槽可以混用吗?

Q:作用域插槽和具名插槽可以混用吗?

Q:作用域插槽有什么实际应用场景?

[Q:Vue 2 和 Vue 3 的插槽有什么区别?](#Q:Vue 2 和 Vue 3 的插槽有什么区别?)

[Q: 具名插槽和作用域插槽的 v-slot 有什么不一样?](#Q: 具名插槽和作用域插槽的 v-slot 有什么不一样?)

[Q:多个元素填同一个插槽必须要用 template 吗?](#Q:多个元素填同一个插槽必须要用 template 吗?)


一、什么是插槽?

一句话:插槽就是组件里留的"坑位",让父组件可以往里面填内容。

复制代码
┌─────────────────────────────────────────────────────────────┐
│                     卡片组件(Card)                          │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  ┌─────────────────────────────────────────────────┐│   │
│  │  │              插槽:放什么就显示什么               ││   │
│  │  └─────────────────────────────────────────────────┘│   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  父组件使用:                                                │
│  <Card>这里的内容会出现在插槽位置</Card>                      │
└─────────────────────────────────────────────────────────────┘

二、插槽的 4 种类型

类型 说明 使用场景
默认插槽 一个坑位,放默认内容 简单的内容插入
具名插槽 多个坑位,每个有名字 复杂布局(头部、内容、底部)
作用域插槽 子组件数据传给父组件 父组件需要子组件的数据来渲染
动态插槽 插槽名可以动态变化 根据条件显示不同插槽

三、默认插槽

子组件(Card.vue)

javascript 复制代码
<template>
  <div class="card">
    <div class="card-header">卡片标题</div>
    <div class="card-body">
      <!-- 默认插槽:父组件放什么,这里就显示什么 -->
      <slot></slot>
    </div>
  </div>
</template>

父组件使用

javascript 复制代码
<template>
  <Card>
    <!-- 这些内容会填入默认插槽 -->
    <p>这是卡片的内容</p>
    <button>点击按钮</button>
  </Card>
</template>

渲染结果:

javascript 复制代码
<div class="card">
  <div class="card-header">卡片标题</div>
  <div class="card-body">
    <p>这是卡片的内容</p>
    <button>点击按钮</button>
  </div>
</div>

四、具名插槽

当组件有多个位置需要填充时,使用具名插槽。

子组件(Layout.vue)

javascript 复制代码
<template>
  <div class="layout">
    <header>
      <!-- 名字叫 header 的插槽 -->
      <slot name="header">默认头部</slot>
    </header>
    
    <main>
      <!-- 默认插槽 -->
      <slot></slot>
    </main>
    
    <footer>
      <!-- 名字叫 footer 的插槽 -->
      <slot name="footer">默认底部</slot>
    </footer>
  </div>
</template>

父组件使用

html 复制代码
<template>
  <Layout>
    <!-- 具名插槽:用 # 或 v-slot -->
    <template #header>
      <h1>自定义头部</h1>
    </template>
    
    <!-- 默认插槽 -->
    <p>主要内容区域</p>
    
    <template #footer>
      <p>自定义底部</p>
    </template>
  </Layout>
</template>

渲染结果:

html 复制代码
<div class="layout">
  <header><h1>自定义头部</h1></header>
  <main><p>主要内容区域</p></main>
  <footer><p>自定义底部</p></footer>
</div>

五、作用域插槽

子组件可以把数据传给父组件,让父组件决定如何渲染。

子组件(UserList.vue)

html 复制代码
<template>
  <ul>
    <li v-for="user in users" :key="user.id">
      <!-- 把 user 数据传给父组件 -->
      <slot name="user" :user="user" :index="index">
        <!-- 默认显示方式 -->
        {{ user.name }}
      </slot>
    </li>
  </ul>
</template>

<script setup>
const users = [
  { id: 1, name: '张三', age: 18 },
  { id: 2, name: '李四', age: 20 },
  { id: 3, name: '王五', age: 22 }
]
</script>

父组件使用

html 复制代码
<template>
  <UserList>
    <!-- 作用域插槽:可以拿到子组件传来的 user 数据 -->
    <template #user="{ user, index }">
      <div class="user-card">
        <span>{{ index + 1 }}.</span>
        <strong>{{ user.name }}</strong>
        <span>({{ user.age }}岁)</span>
      </div>
    </template>
  </UserList>
</template>

渲染结果:

html 复制代码
<ul>
  <li><div class="user-card"><span>1.</span><strong>张三</strong><span>(18岁)</span></div></li>
  <li><div class="user-card"><span>2.</span><strong>李四</strong><span>(20岁)</span></div></li>
  <li><div class="user-card"><span>3.</span><strong>王五</strong><span>(22岁)</span></div></li>
</ul>

六、动态插槽

插槽名可以根据变量动态变化。

html 复制代码
<template>
  <Layout>
    <!-- 动态插槽名 -->
    <template #[slotName]>
      动态插槽内容
    </template>
  </Layout>
</template>

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

const slotName = ref('header')
// 可以动态切换:'header'、'content'、'footer'
</script>

七、完整示例:弹窗组件

html 复制代码
<!-- Modal.vue -->
<template>
  <div v-if="visible" class="modal">
    <div class="modal-container">
      <div class="modal-header">
        <slot name="header">
          <h3>默认标题</h3>
        </slot>
        <button class="close" @click="handleClose">×</button>
      </div>
      
      <div class="modal-body">
        <!-- 默认插槽:放内容 -->
        <slot>默认内容</slot>
      </div>
      
      <div class="modal-footer">
        <slot name="footer">
          <button @click="handleClose">取消</button>
          <button @click="handleConfirm">确定</button>
        </slot>
      </div>
    </div>
  </div>
</template>

<script setup>
const props = defineProps(['visible'])
const emit = defineEmits(['update:visible', 'confirm'])

function handleClose() {
  emit('update:visible', false)
}

function handleConfirm() {
  emit('confirm')
  handleClose()
}
</script>
html 复制代码
<!-- 父组件使用 -->
<template>
  <button @click="showModal = true">打开弹窗</button>
  
  <Modal v-model:visible="showModal" @confirm="handleConfirm">
    <template #header>
      <h3 style="color: red;">删除确认</h3>
    </template>
    
    <p>确定要删除这条数据吗?</p>
    <p class="warning">此操作不可恢复!</p>
    
    <template #footer>
      <button class="cancel" @click="showModal = false">我再想想</button>
      <button class="confirm" @click="handleConfirm">确认删除</button>
    </template>
  </Modal>
</template>

八、插槽对比总结

插槽类型 写法(子组件) 写法(父组件) 数据流向
默认插槽 <slot></slot> 直接放内容 父→子
具名插槽 <slot name="xxx"> <template #xxx> 父→子
作用域插槽 <slot :data="data"> <template #default="{ data }"> 子→父

九、面试高频问题

Q:插槽是什么?有什么作用?

答:

插槽是 Vue 组件中预留的"坑位",让父组件可以向子组件注入内容。它让组件更灵活、更可复用。

作用:

  • 组件内容可定制

  • 提高组件的复用性

  • 实现复杂的布局组件

Q:具名插槽和作用域插槽的区别?

答:

  • 具名插槽:给插槽起名字,父组件可以往指定位置填内容(数据从父→子)

  • 作用域插槽:子组件可以把数据传给父组件,让父组件决定如何渲染(数据从子→父)

Q:v-slot 的简写是什么?

答:

v-slot:header 可以简写为 #header

Q:使用插槽时,子组件和父组件都要用template包裹吗?

答:不是。

  • 子组件 :用 <slot> 标签定义插槽,不需要 <template> 包裹

  • 父组件:填充插槽时,根据情况决定:

    • 单个元素填默认插槽:可以不用 <template>

    • 多个元素填同一个插槽:需要用 <template> 包裹

    • 填具名插槽或作用域插槽:需要用 <template #插槽名>

Q:插槽可以有默认内容吗?

可以。<slot>默认内容</slot>,如果父组件不传内容,就显示默认内容。

Q:默认插槽和具名插槽可以混用吗?

可以。组件里可以有多个具名插槽和一个默认插槽。

Q:作用域插槽和具名插槽可以混用吗?

可以。作用域插槽可以有名字。

Q:作用域插槽有什么实际应用场景?

比如一个表格组件,子组件提供数据,父组件可以自定义每一列的显示方式(文本、按钮、图片等)。或者一个列表组件,父组件可以自定义每个列表项的样式。

Q:Vue 2 和 Vue 3 的插槽有什么区别?

Vue 3 中 v-slot 只能用在 <template> 上,不能用在普通元素上。写法上更规范了。

Q: 具名插槽和作用域插槽的 v-slot 有什么不一样?

答:

两者都是 v-slot,但作用不同、写法不同

  • 具名插槽 :用于匹配子组件中指定名字的插槽,不接收数据 。写法是 #headerv-slot:header

  • 作用域插槽 :用于接收子组件传过来的数据,父组件可以自定义渲染方式。写法是 #default="{ data }"#item="{ item }"

核心区别:具名插槽是父组件告诉子组件"内容放哪里";作用域插槽是子组件告诉父组件"这里有数据,你来决定怎么显示"。

Q:多个元素填同一个插槽必须要用 template 吗?

答:是的。

对于默认插槽

  • 单个元素可以不用 <template>,直接写

  • 多个元素必须用 <template> 包裹

对于具名插槽作用域插槽

  • 无论单个还是多个元素,都必须用 <template> 包裹

这是因为 Vue 只能传递单个根节点给插槽,用 <template> 可以把多个元素组合成一个整体。

相关推荐
一 乐2 小时前
咖啡商城|基于springboot + vue咖啡商城系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·咖啡商城系统
学习使我健康2 小时前
Android 事件分发机制
android·java·前端
众少成多积小致巨2 小时前
libbinder_ndk 入门指南
前端·c++·架构
小李子呢02112 小时前
前端八股Vue---自定义组件(控件)
前端·javascript·vue.js
用户52709648744902 小时前
微前端(qiankun)单侧启动调试技巧
前端
于慨2 小时前
flutter基础组件用法
开发语言·javascript·flutter
斌味代码2 小时前
jQuery 内存泄漏排查:常见场景、工具使用与修复实战
前端·javascript·jquery
weixin199701080162 小时前
《爱回收商品详情页前端性能优化实战》
前端·性能优化
chenbin___2 小时前
鸿蒙(HarmonyOS)支持 useNativeDriver的详细说明(转自千问)
前端·javascript·react native·react.js·harmonyos