文章目录
在 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>
- 模态框组件
<!-- 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] | 根据条件动态切换插槽位置 |