Code前端首页关于Code前端联系我们

想搞明白Vue3里slot咋用?别慌,这篇从基础到进阶给你掰碎了讲。不管是刚学Vue3的新手,还是想补插槽知识的同学,看完这些常见问题和例子,你对slot的理解肯定能更透~

terry 2周前 (09-29) 阅读数 44 #Vue
文章标签 Vue3 slot

插槽(Slot)是干啥的?Vue3里基础插槽咋写?

插槽本质是Vue里的「内容分发机制」——子组件留个“坑”,父组件往这个坑里塞内容,这样做能让组件更灵活,比如写个按钮组件,不同页面按钮里的文字、图标可能不一样,用插槽就能让父组件自由决定按钮里放啥。

Vue3里基础插槽(匿名插槽)写法特简单:
子组件(比如叫MyButton.vue)里放<slot></slot>,这就是留给父组件塞内容的位置,要是父组件没塞内容,<slot>里还能写默认内容,比如<slot>默认按钮</slot>

父组件用的时候,直接在<MyButton>标签里写内容就行:

<MyButton>  
  <span style="color:red">我是自定义按钮文字</span>  
  <img src="icon.png" />  
</MyButton>  

这样子组件的<slot>位置就会被父组件塞的内容替换掉,要是父组件没写内容,就显示“默认按钮”~

多个插槽咋区分?具名插槽(Named Slots)咋用?

当子组件需要多个插槽时,光用匿名插槽不够用了,这时候得给插槽起名字,也就是「具名插槽」,比如写个页面布局组件Layout.vue,需要header、主体、footer三个区域,每个区域都是插槽。

子组件里给<slot>name属性:

<template>  
  <div class="layout">  
    <header>  
      <slot name="header"></slot>  
    </header>  
    <main>  
      <slot name="main"></slot>  
    </main>  
    <footer>  
      <slot name="footer"></slot>  
    </footer>  
  </div>  
</template>  

父组件要给不同名字的插槽塞内容,得用<template>配合v-slot指令(简写),比如给header插槽塞内容:

<Layout>  
  <template #header>  
    <h1>这是页面标题</h1>  
  </template>  
  <template #main>  
    <p>这里是正文内容...</p>  
  </template>  
  <template #footer>  
    <p>© 2024 版权信息</p>  
  </template>  
</Layout>  

注意哦,v-slot:name 可以简写成#name,比如#header等价于v-slot:header~ 这样每个具名插槽对应父组件里的<template>内容,子组件里的位置就会被精准替换~

子组件数据想给父组件插槽用?作用域插槽(Scoped Slots)安排上!

有时候子组件里有数据,想让父组件在插槽里能用到这些数据,这时候就得用「作用域插槽」,举个例子:子组件TodoList.vue里循环渲染todo列表,每个todo的内容是子组件里的数据,但父组件想自定义每个todo的样式(比如加个 checkbox、改文字颜色)。

子组件里,把数据通过slot的属性传出去:

<template>  
  <ul>  
    <li v-for="todo in todos" :key="todo.id">  
      <slot :todo="todo"></slot> <!-- 把todo数据传给父组件插槽 -->  
    </li>  
  </ul>  
</template>  
<script setup>  
import { ref } from 'vue'  
const todos = ref([  
  { id: 1, text: '学习Vue3', done: false },  
  { id: 2, text: '写代码', done: false }  
])  
</script>  

父组件接收这些数据,得用v-slot绑定一个变量(也能解构):

<TodoList>  
  <template #default="slotProps"> <!-- default是匿名插槽的名字,也能省略#default -->  
    <input type="checkbox" v-model="slotProps.todo.done" />  
    <span :style="{ color: slotProps.todo.done ? 'gray' : 'black' }">  
      {{ slotProps.todo.text }}  
    </span>  
  </template>  
</TodoList>  

或者更简洁的解构写法:

<template #default="{ todo }">  
  <input type="checkbox" v-model="todo.done" />  
  <span :style="{ color: todo.done ? 'gray' : 'black' }">  
    {{ todo.text }}  
  </span>  
</template>  

这样父组件插槽里就能拿到子组件传的todo数据,自由定制渲染方式,是不是很灵活?

插槽也能动态切换?动态插槽名咋实现?

有时候插槽该显示哪个,得根据变量动态决定,这时候「动态插槽名」就派上用场了,Vue3里用v-slot:[变量名]的语法就能实现。

举个例子:父组件有个变量currentSlot,值可能是'left''right',子组件里有对应的具名插槽<slot name="left"><slot name="right">

父组件代码:

<template>  
  <script setup>  
    import { ref } from 'vue'  
    const currentSlot = ref('left') // 可以动态切换成'right'  
  </script>  
  <MyComponent>  
    <template v-slot:[currentSlot]>  
      <!-- 这里内容会根据currentSlot切换到对应具名插槽 -->  
      {{ currentSlot === 'left' ? '左边内容' : '右边内容' }}  
    </template>  
  </MyComponent>  
</template>  

子组件MyComponent.vue

<template>  
  <div>  
    <slot name="left">左边默认内容</slot>  
    <slot name="right">右边默认内容</slot>  
  </div>  
</template>  

这样当currentSlot变化时,父组件会自动切换给哪个具名插槽塞内容,子组件对应位置也会跟着变~ 动态场景下特别好用,比如tab切换、权限控制显示不同区域~

插槽的默认内容咋设置?不想传内容时能显示默认!

有时候父组件可能忘了给插槽传内容,这时候子组件里的<slot>可以设置默认内容,避免页面空着不好看。

写法很简单:子组件里<slot>标签中间写内容,就是默认内容,比如做个提示框组件Alert.vue

<template>  
  <div class="alert">  
    <slot>这是默认提示文本</slot>  
  </div>  
</template>  

父组件用的时候,如果没写内容,就显示默认:

<Alert /> <!-- 显示“这是默认提示文本” -->  

如果父组件传了内容,就替换默认:

<Alert>  
  这是自定义的提示内容~  
</Alert>  

机制,能让组件更健壮,就算父组件没传内容也不会崩,还能保持UI一致性~

插槽和组件通信有啥不一样?啥场景用slot更合适?

很多刚学Vue的同学会纠结:props/emit也能传数据、传事件,为啥还要用slot?其实它们的「分工」不一样:

  • props/emit:主打「数据和事件的通信」,父传子用props,子传父用emit,重点在「数据流转」。
  • 插槽(Slot):主打「内容和结构的分发」,父组件决定子组件里某块区域长啥样,重点在「UI定制」。

举几个适合用slot的场景:

  1. 布局组件:比如页面框架(header、footer、侧边栏),父组件自由决定每个区域的内容。
  2. 列表项自定义:像表格、下拉列表,子组件负责数据循环,父组件自定义每一项的渲染(比如表格列的内容、样式)。
  3. 通用组件扩展:比如弹窗组件,父组件决定弹窗里的表单、按钮怎么放;按钮组件,父组件决定按钮里的图标、文字组合。

举个实际例子:UI库的Table组件,列的内容需要自定义,子组件Table里循环渲染行,每行的列数据通过作用域插槽传给父组件,父组件用<template #column="{ row, column }">来自定义每个单元格的内容(比如给姓名列加tooltip,给操作列加按钮),这种场景下,用插槽比props传一堆渲染函数要直观太多~

再总结下:当你需要「让父组件决定子组件某部分长啥样」时,优先考虑slot;当你需要「父组件给子组件传数据/子组件通知父组件事件」时,用props/emit~ 两者结合起来,组件的灵活性直接拉满~

现在再回头看,Vue3的slot其实就围绕「内容分发」展开:基础插槽管单区域,具名插槽管多区域,作用域插槽管子传父数据给插槽用,动态插槽管插槽名动态切换,默认内容管兜底,把这些用法和场景吃透,写组件时想咋灵活就咋灵活~ 要是练手的话,不妨从写个带插槽的按钮、布局组件开始,实操几次就熟啦~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门