vue 插槽详解


在 Vue.js 中,插槽(Slots) 是一种强大的内容分发机制,允许父组件向子组件传递模板片段,实现更灵活的组件复用和布局控制。以下是 Vue 插槽的详细解析,涵盖基础用法、高级特性及实际场景示例。

一、基础插槽

1. 默认插槽(Default Slot)

子组件通过 标签定义插槽位置,父组件可在子组件标签内填充内容:

<!-- 子组件 ChildComponent.vue -->
<template>
  <div class="child">
    <h2>子组件标题</h2>
    <slot>这是默认内容(当父组件未填充时显示)</slot>
  </div>
</template>
 
<!-- 父组件使用 -->
<template>
  <ChildComponent>
    <p>这是父组件传递的内容,会替换默认插槽</p>
  </ChildComponent>
</template>

2. 插槽作用域

父组件模板中的所有内容 都在父组件作用域中编译,子组件模板中的内容 在子组件作用域中编译。
插槽内可以访问父组件的数据,但无法直接访问子组件的数据(需通过作用域插槽解决)。

二、具名插槽

当子组件有多个插槽时,可通过 name 属性区分,父组件使用 v-slot 指令指定内容分发位置。

1. 子组件定义具名插槽

<!-- 子组件 LayoutComponent.vue -->
<template>
  <div class="layout">
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot></slot> <!-- 默认插槽,可省略name -->
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template>

2. 父组件分发内容

<!-- 父组件 -->
<template>
  <LayoutComponent>
    <template v-slot:header>
      <h1>页面标题</h1>
    </template>
 
    <!-- 默认插槽可简写为 <template #default> -->
    <p>这是主内容区</p>
 
    <template v-slot:footer>
      <p>版权信息 © 2023</p>
    </template>
  </LayoutComponent>
</template>

3. 简写语法

v-slot:header 可简写为 #header(仅限具名插槽)。
默认插槽简写:#default 或直接省略 标签(不推荐,可读性差)。

三、作用域插槽

当子组件需要向插槽内容暴露数据时,使用作用域插槽(Scoped Slots),通过 slot-props 将数据传递给父组件。

1. 子组件暴露数据

<!-- 子组件 DataList.vue -->
<template>
  <ul>
    <li v-for="item in items" :key="item.id">
      <!-- 将item作为slot-props传递给父组件 -->
      <slot :item="item" :index="index"></slot>
    </li>
  </ul>
</template>
 
<script>
export default {
  data() {
    return {
      items: [
        { id: 1, name: 'Apple' },
        { id: 2, name: 'Banana' }
      ]
    };
  }
};
</script>

2. 父组件接收数据并渲染

<!-- 父组件 -->
<template>
  <DataList>
    <!-- 使用解构语法接收slot-props -->
    <template #default="{ item, index }">
      <span>{{ index + 1 }}. {{ item.name }}</span>
    </template>
  </DataList>
</template>

3. 动态作用域插槽

结合计算属性或方法动态生成插槽内容:

<template>
  <DataList>
    <template #default="{ item }">
      <button @click="handleClick(item)">
        操作 {{ item.name }}
      </button>
    </template>
  </DataList>
</template>
 
<script>
export default {
  methods: {
    handleClick(item) {
      console.log('点击了:', item.name);
    }
  }
};
</script>

四、高级用法

1. 动态插槽名

通过 v-slot:[dynamicSlotName] 实现动态分发:

<template>
  <ChildComponent>
    <template v-slot:[currentSlot]>
      动态插槽内容
    </template>
  </ChildComponent>
</template>
 
<script>
export default {
  data() {
    return {
      currentSlot: 'header' // 可动态修改为 'footer' 等
    };
  }
};
</script>

2. 解构默认值

当插槽属性可能为 undefined 时,可设置默认值:

<template #default="{ item = { name: '默认值' }, index = 0 }">
  {{ index }}: {{ item.name }}
</template>

五、实际应用场景

1. 通用布局组件

<!-- 通用布局 PageLayout.vue -->
<template>
  <div class="page">
    <slot name="sidebar"></slot>
    <div class="content">
      <slot></slot>
    </div>
  </div>
</template>
 
<!-- 父组件使用 -->
<template>
  <PageLayout>
    <template #sidebar>
      <SidebarMenu />
    </template>
    <div>主内容区</div>
  </PageLayout>
</template>

2. 表格组件封装

<!-- 封装可复用的表格组件 SmartTable.vue -->
<template>
  <table>
    <thead>
      <tr>
        <th v-for="col in columns" :key="col.key">
          {{ col.title }}
        </th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="(row, rowIndex) in data" :key="row.id">
        <td v-for="col in columns" :key="col.key">
          <slot :name="col.slot" :row="row" :index="rowIndex">
            {{ row[col.key] }} <!-- 默认显示原始数据 -->
          </slot>
        </td>
      </tr>
    </tbody>
  </table>
</template>
 
<script>
export default {
  props: {
    columns: Array, // 列配置,如 [{ key: 'name', title: '姓名', slot: 'name' }]
    data: Array
  }
};
</script>
 
<!-- 父组件自定义单元格 -->
<template>
  <SmartTable :columns="columns" :data="users">
    <template #name="{ row }">
      <router-link :to="`/user/${row.id}`">{{ row.name }}</router-link>
    </template>
    <template #status="{ row }">
      <el-tag :type="row.status === 'active' ? 'success' : 'danger'">
        {{ row.status }}
      </el-tag>
    </template>
  </SmartTable>
</template>
  1. 模态框组件
<!-- Modal.vue -->
<template>
  <div class="modal" v-if="visible">
    <div class="modal-header">
      <slot name="header">
        <h3>默认标题</h3>
      </slot>
      <button @click="$emit('close')">×</button>
    </div>
    <div class="modal-body">
      <slot></slot>
    </div>
    <div class="modal-footer">
      <slot name="footer">
        <button @click="$emit('confirm')">确认</button>
      </slot>
    </div>
  </div>
</template>
 
<!-- 父组件自定义内容 -->
<template>
  <Modal v-model="showModal">
    <template #header>
      <h2>自定义标题</h2>
    </template>
    <p>这是模态框主体内容</p>
    <template #footer>
      <button @click="showModal = false">取消</button>
      <button @click="submit">提交</button>
    </template>
  </Modal>
</template>

六、注意事项

1.兼容性

  • Vue 2.6+ 推荐使用 v-slot 语法,旧版本可使用 slot 和 slot-scope 属性(已废弃)。
  • Vue 3 完全移除了 slot-scope,仅支持 v-slot。

2.性能优化:

  • 避免在插槽内编写复杂逻辑,可能导致不必要的重新渲染。
  • 对于大量动态内容,考虑使用 v-once 或 key 优化。
  • 样式隔离:
    插槽内容默认继承父组件样式,可通过 CSS 作用域(scoped)或 CSS Modules 控制。

3.总结

通过灵活运用插槽,可以构建出高度可复用、可定制的组件库,显著提升开发效率和代码质量。

特性语法适用场景
默认插槽<slot></slot>简单内容分发
具名插槽<slot name="header"></slot>多区域布局(如页眉/页脚)
作用域插槽<slot :item="data"></slot>子组件向父组件传递数据
动态插槽名v-slot:[dynamicName]根据条件动态切换插槽位置
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木易 士心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值