在 Vue 中,插槽(slot)是一种强大的机制,用于在父组件中将内容传递到子组件的特定位置。Vue 插槽有助于实现组件内容的灵活性和复用性。
插槽的使用方法
- 基本插槽:基本插槽用于在父组件中向子组件传递内容。
//子组件(MyComponent.vue)
<template>
<div class="my-component">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'MyComponent'
}
</script>
<style scoped>
.my-component {
border: 1px solid #ccc;
padding: 10px;
}
</style>
//父组件
<template>
<div>
<MyComponent>
<p>This is some content from the parent component.</p>
</MyComponent>
</div>
</template>
<script setup>
import MyComponent from './MyComponent.vue'
</script>
- 具名插槽:具名插槽允许在子组件中定义多个插槽,每个插槽有一个名称。
//子组件
<template>
<div class="my-component">
<slot name="header"></slot>
<slot></slot>
<slot name="footer"></slot>
</div>
</template>
<script>
export default {
name: 'MyComponent'
}
</script>
<style scoped>
.my-component {
border: 1px solid #ccc;
padding: 10px;
}
</style>
//父组件
<template>
<div>
<MyComponent>
<template #header>
<h1>Header Content</h1>
</template>
<p>Main Content</p>
<template #footer>
<footer>Footer Content</footer>
</template>
</MyComponent>
</div>
</template>
<script setup>
import MyComponent from './MyComponent.vue'
</script>
- 作用域插槽:作用域插槽允许父组件访问子组件的数据,并根据这些数据渲染内容。
//子组件
<template>
<div class="my-component">
<slot :item="item"></slot>
</div>
</template>
<script setup>
import { reactive } from 'vue'
const item = reactive({
name: 'Vue.js',
version: '3.0'
})
</script>
<style scoped>
.my-component {
border: 1px solid #ccc;
padding: 10px;
}
</style>
//父组件
<template>
<div>
<MyComponent v-slot="{ item }">
<p>Name: {{ item.name }}</p>
<p>Version: {{ item.version }}</p>
</MyComponent>
</div>
</template>
<script setup>
import MyComponent from './MyComponent.vue'
</script>
使用场景
- 内容分发和复用:插槽可以让父组件将不同的内容插入到子组件中,提升组件的复用性。例如,一个通用的对话框组件可以使用插槽插入不同的标题和内容。
- 组件之间的解耦:插槽让父组件和子组件之间的依赖关系更松散,子组件无需知道父组件提供的具体内容。
- 灵活的布局:通过具名插槽,父组件可以灵活地控制子组件内部的布局。例如,一个布局组件可以使用具名插槽定义不同区域(头部、内容区、底部)的内容。
- 数据传递:作用域插槽让父组件可以访问子组件的数据,适用于需要根据子组件数据动态渲染内容的场景。
插槽的高级用法
- 默认插槽内容:子组件可以定义默认的插槽内容,当父组件未提供内容时显示
//子组件
<template>
<div class="my-component">
<slot>This is default content</slot>
</div>
</template>
<script setup>
</script>
<style scoped>
.my-component {
border: 1px solid #ccc;
padding: 10px;
}
</style>
- 动态插槽名:可以使用动态插槽名来决定渲染哪个插槽。
//子组件
<template>
<div class="my-component">
<slot :name="dynamicSlot"></slot>
</div>
</template>
<script setup>
import { toRefs } from 'vue'
const props = defineProps({
dynamicSlot: {
type: String,
required: true
}
})
</script>
<style scoped>
.my-component {
border: 1px solid #ccc;
padding: 10px;
}
</style>
//父组件
<template>
<div>
<MyComponent :dynamicSlot="currentSlot">
<template #slotA>
<p>This is Slot A</p>
</template>
<template #slotB>
<p>This is Slot B</p>
</template>
</MyComponent>
<button @click="currentSlot = 'slotA'">Show Slot A</button>
<button @click="currentSlot = 'slotB'">Show Slot B</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import MyComponent from './MyComponent.vue'
const currentSlot = ref('slotA')
</script>
总结
Vue 插槽提供了灵活且强大的方式来构建可复用和动态的组件。无论是简单的内容分发还是复杂的数据传递和布局控制,插槽都能很好地满足这些需求。在 Vue 3 中,插槽的使用变得更加简洁和直观,通过结合 Composition API,可以更好地管理组件的逻辑和状态。
完整实例代码
<!-- 默认插槽用于传递组件中的默认内容,适用于简单的内容传递。 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue Default Slot Example</title>
<script src="https://unpkg.com/vue@3.2.47/dist/vue.global.prod.js"></script>
</head>
<body>
<div id="app">
<default-slot-example>
<p>这是一个默认插槽!!!</p>
</default-slot-example>
</div>
<script>
const DefaultSlotExample = {
template: `
<div>
<h1>Default Slot Example</h1>
<slot></slot>
</div>
`
};
const app = Vue.createApp({
components: {
DefaultSlotExample
}
});
app.mount('#app');
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue Named Slot Example</title>
<script src="https://unpkg.com/vue@3.2.47/dist/vue.global.prod.js"></script>
</head>
<body>
<div id="app">
<named-slot-example>
<template v-slot:header>
<h1>This is header content</h1>
</template>
<template v-slot:footer>
<p>This is footer content</p>
</template>
</named-slot-example>
</div>
<script>
const NamedSlotExample = {
template: `
<div>
<header>
<slot name="header"></slot>
</header>
<main>
<p>Main content goes here</p>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
`
};
const app = Vue.createApp({
components: {
NamedSlotExample
}
});
app.mount('#app');
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue Scoped Slot Example</title>
<script src="https://unpkg.com/vue@3.2.47/dist/vue.global.prod.js"></script>
</head>
<body>
<div id="app">
<scoped-slot-example>
<template v-slot:default="slotProps">
<p>User: {{ slotProps.user.name }}</p>
<p>Age: {{ slotProps.user.age }}</p>
</template>
</scoped-slot-example>
</div>
<script>
const ScopedSlotExample = {
template: `
<div>
<slot :user="user"></slot>
</div>
`,
data () {
return {
user: {
name: 'Alice',
age: 25
}
};
}
};
const app = Vue.createApp({
components: {
ScopedSlotExample
}
});
app.mount('#app');
</script>
</body>
</html>