插槽
- 插槽 API
- Vue 的插槽(Slot)是实现组件内容分发的一种机制,允许父组件向子组件传递自定义的内容。传什么内容,子组件就渲染什么样的内容,对组件添加属性更灵活(通过组件属性也是没有办法传递html标签的)。所以插槽是一种传递复杂内容的一种方式,通过属性是无法传递的,所以设计了插槽API.
- v-slot 指令自 Vue 2.6.0 起被引入,提供更好的支持 slot 和 slot-scope attribute 的 API 替代方案
- 如果组件中的 template 中没有包含一个
<slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。 - 插槽内可以包含任何模板代码。
- 一个不带 name 的
<slot>
出口会带有隐含的名字“default”
。
默认插槽(Default Slot)
是最简单的插槽形式,用于在子组件中预留一个位置,供父组件插入内容。子组件中通过 <slot></slot>
定义插槽位置
<!-- 子组件 ChildComponent.vue -->
<template>
<div class="child">
<slot>默认内容</slot>
</div>
</template>
<!-- 父组件 ParentComponent.vue -->
<template>
<ChildComponent>
<p>这是父组件传递的内容</p>
</ChildComponent>
</template>
具名插槽(Named Slot)
- 当需要多个插槽时,可以通过 name 属性区分不同的插槽,
- 子组件中通过
<slot name="插槽名称"></slot>
定义具名插槽。 - 父组件通过
<template v-slot:插槽名称>
绑定内容
<!-- 子组件 ChildComponent.vue -->
<template>
<div class="child">
<slot name="header">默认头部内容</slot>
<slot>默认主体内容</slot>
<slot name="footer">默认底部内容</slot>
</div>
</template>
<!-- 父组件 ParentComponent.vue -->
<template>
<ChildComponent>
<template v-slot:header>
<h1>这是头部内容</h1>
</template>
<p>这是主体内容</p>
<template v-slot:footer>
<p>这是底部内容</p>
</template>
</ChildComponent>
</template>
作用域插槽(Scoped Slot)
- 作用域插槽允许子组件向父组件传递数据,并让父组件决定如何渲染这些数据。
- 子组件通过 标签的 v-bind 属性传递数据。
- 父组件通过 v-slot 接收并使用这些数据。
<!-- 子组件 ChildComponent.vue -->
<template>
<div class="child">
<slot :user="user" :message="message"></slot>
</div>
</template>
<script>
export default {
data() {
return {
user: { name: '张三', age: 25 },
message: '欢迎来到 Vue 插槽世界!'
};
}
};
</script>
<!-- 父组件 ParentComponent.vue -->
<template>
<ChildComponent v-slot="{ user, message }">
<p>用户名:{{ user.name }}</p>
<p>年龄:{{ user.age }}</p>
<p>{{ message }}</p>
</ChildComponent>
</template>
案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
{{message}} {{message + message}}
<div :id="message"></div>
<todo-list>
<!--
todo-list 组件中不设置 slot 标签时, vue 是不知道标签 todo-item 挂在 标签 todo-list 下的
在 todo-list 组件中, 添加一个 slot 标签时,就可以由开发者自定义一些渲染内容了
-->
<todo-item @delete="handleDelete" v-for="item in list" :title="item.title" :del="item.del">
<!--
插槽本质上是一个函数,在组件 todo-item 中 通过 slot 标签调用了这个函数,并给这个函数传递了一个参数,也就是 value 值
子组件可以传递给父组件内容的插槽叫做作用域插槽
-->
<template v-slot:pre-icon="{value}">
<span v-if="value>0.5" style="color: red">红色图标 {{value}} </span>
<span v-else style="color: green">绿色图标 {{value}} </span>
</template>
<!-- 如果指定了插槽时,则替换组件中 suf-icon 的默认的插槽的内容-->
<template v-slot:suf-icon>
<span>后置图标 😄</span>
</template>
</todo-item>
</todo-list>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('todo-item', {
props: {
title: String,
del: {
type: Boolean,
default: false,
},
},
template:
`
<li>
<slot name="pre-icon" :value="value"></slot>
<span v-if="!del">{{ title }}</span>
<span v-else style="text-decoration: line-through">{{ title }}</span>
<slot name="suf-icon">默认插槽值:0</slot>
<button v-show="!del" @click="handleClick">删除</button>
</li>
`,
data: function () {
return {
value: Math.random()
}
},
methods: {
handleClick(e) {
console.log('点击删除按钮')
this.$emit('delete', this.title)
}
},
})
Vue.component('todo-list', {
template:
`
<ul>
<slot></slot>
</ul>
`,
data: function () {
return {}
},
})
var vm = new Vue({
el: '#app',
data: {
message: 'hello world',
list: [{
title: '课程1',
del: false
}, {
title: '课程2',
del: true
}],
},
methods: {
handleDelete(val) {
console.log('handleDelete', val)
}
}
})
</script>
</body>
</html>