前言
不使用卡槽,子组件的内容是固定的,无法在父组件中进行灵活的插入和替换
<!-- ChildComponent.vue -->
<template>
<div>
<p>This is fixed content</p>
</div>
</template>
使用卡槽的好处:父组件可以灵活地插入内容,使得子组件变得更加通用和可重用
<!-- ChildComponent.vue -->
<template>
<div>
<slot></slot>
</div>
</template>
1. 基本知识
卡槽(Slot)是用于在父组件中定义子组件内容的一种机制
卡槽允许在使用组件时插入内容,从而使组件更加灵活和可重用
基本用法如下:
<!-- ParentComponent.vue -->
<template>
<ChildComponent>
<p>This is some default slot content</p>
</ChildComponent>
</template>
<!-- ChildComponent.vue -->
<template>
<div>
<slot></slot>
</div>
</template>
具名卡槽:(在组件中定义多个插槽,并为每个插槽指定一个名称)
<!-- ParentComponent.vue -->
<template>
<ChildComponent>
<template v-slot:header>
<h1>This is the header</h1>
</template>
<template v-slot:footer>
<p>This is the footer</p>
</template>
</ChildComponent>
</template>
<!-- ChildComponent.vue -->
<template>
<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot> <!-- Default slot -->
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
作用域插槽:将数据从子组件传递给父组件使用的插槽内容
ChildComponent 向默认插槽传递了一个 message 属性
父组件 ParentComponent 使用 slotProps 访问并显示该消息
<!-- ParentComponent.vue -->
<template>
<ChildComponent>
<template v-slot:default="slotProps">
<p>{{ slotProps.message }}</p>
</template>
</ChildComponent>
</template>
<!-- ChildComponent.vue -->
<template>
<div>
<slot :message="message"></slot>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello from ChildComponent'
};
}
};
</script>
2. 卡槽属性
插槽属性允许将数据从子组件传递给父组件中的插槽内容,这些值在使用作用域插槽(Scoped Slots)时特别有用
允许带上的数值:字符串、数字、布尔值、对象、数组
基础字符串和数字值:
子组件:
<!-- ChildComponent.vue -->
<template>
<div>
<slot :title="title" :count="count"></slot>
</div>
</template>
<script>
export default {
data() {
return {
title: 'Hello from ChildComponent',
count: 42
};
}
};
</script>
父组件:
<!-- ParentComponent.vue -->
<template>
<ChildComponent>
<template v-slot="{ title, count }">
<h1>{{ title }}</h1>
<p>Count: {{ count }}</p>
</template>
</ChildComponent>
</template>
对象和数组:
子组件:
<!-- ChildComponent.vue -->
<template>
<div>
<slot :user="user" :items="items"></slot>
</div>
</template>
<script>
export default {
data() {
return {
user: { name: 'John Doe', age: 30 },
items: ['item1', 'item2', 'item3']
};
}
};
</script>
父组件:
<!-- ParentComponent.vue -->
<template>
<ChildComponent>
<template v-slot="{ user, items }">
<p>User: {{ }}, Age: {{ user.age }}</p>
<ul>
<li v-for="item in items" :key="item">{{ item }}</li>
</ul>
</template>
</ChildComponent>
</template>
3. Demo1
<!-- ParentComponent.vue -->
<template>
<Card>
<template v-slot:header>
<h1>Card Header</h1>
</template>
<template v-slot:default="slotProps">
<p>{{ slotProps.content }}</p>
</template>
<template v-slot:footer>
<p>Card Footer</p>
</template>
</Card>
</template>
<!-- Card.vue -->
<template>
<div class="card">
<header>
<slot name="header"></slot>
</header>
<main>
<slot :content="content"></slot> <!-- Scoped slot -->
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<script>
export default {
data() {
return {
content: 'This is the card content'
};
}
};
</script>
<style>
.card {
border: 1px solid #ccc;
padding: 1rem;
}
</style>
4. Demo2
子组件:
<!-- UserCard.vue -->
<template>
<div class="user-card">
<slot :user="user" :posts="posts"></slot>
</div>
</template>
<script>
export default {
data() {
return {
user: { name: 'Jane Doe', age: 25, location: 'New York' },
posts: [
{ id: 1, title: 'My first post' },
{ id: 2, title: 'Vue is awesome' }
]
};
}
};
</script>
<style>
.user-card {
border: 1px solid #ccc;
padding: 1rem;
border-radius: 4px;
}
</style>
父组件:
<!-- ParentComponent.vue -->
<template>
<UserCard>
<template v-slot="{ user, posts }">
<h2>{{ }}</h2>
<p>Age: {{ user.age }}</p>
<p>Location: {{ user.location }}</p>
<h3>Posts:</h3>
<ul>
<li v-for="post in posts" :key="post.id">{{ post.title }}</li>
</ul>
</template>
</UserCard>
</template>
5. 彩蛋
比较特殊,在实战中遇到
row
和 $index
是 el-table 组件在渲染表格时自动提供的,不需要在父组件中显式定义
属性是 el-table 组件在内部处理数据时生成的,并在每一行的数据传递过程中提供给 el-table-column 组件的插槽
将其转化为Demo所示:
<template>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
v-loading="formLoading"
label-width="0px"
:inline-message="true"
>
<el-table :data="formData" class="-mt-10px">
<el-table-column label="序号" type="index" width="100" />
<el-table-column label="船名" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.shipName`" :rules="formRules.shipName" class="mb-0px!">
<el-input v-model="row.shipName" :disabled="formType === 'detail'" placeholder="请输入船名" />
</el-form-item>
</template>
</el-table-column>
<!-- 其他列定义相同 -->
</el-table>
</el-form>
</template>
<script>
export default {
data() {
return {
formData: [
{ shipName: '', voyage: '', billNumber: '', boxNumber: '', boxSize: '', boxType: '', productName: '', hazardousLevel: '', hazardCode: '' },
// 其他行数据
],
formRules: {
shipName: [{ required: true, message: '请输入船名', trigger: 'blur' }],
// 其他表单验证规则
},
formLoading: false,
formType: 'edit' // 可能是 'edit' 或 'detail'
};
},
methods: {
handleDelete(index) {
this.formData.splice(index, 1);
}
}
};
</script>
<style scoped>
.mb-0px! {
margin-bottom: 0 !important;
}
</style>