源于工作中一次组件的重构,这里重新写一个例子来练习下嵌套 slot 的使用。
嵌套 slot 需要使用到 v-slot 命令,使用该命令需要确保Vue 版本为 2.6.0 +。
完整的代码可见:TodoExamples.
示例
问题描述
现有一个 ToDoList 组件,其子组件 ToDoItem, 组件内容分别如下:
<!-- ToDoItem.vue -->
<template>
<li>
<span class="text-muted mr-1">{{ item.id }}.</span> {{ item.title }}
</li>
</template>
<script>
export default {
props: [ 'item' ]
}
</script>
<!-- ToDoList.vue -->
<template>
<ul>
<ToDoItem v-for="item in items" :key="item.id" :item="item" />
</ul>
</template>
<script>
import ToDoItem from './ToDoItem'
export default {
components: { ToDoItem },
props: [ 'items' ]
}
</script>
在 schedule 中调用 ToDoList 组件:
<!-- schedule.vue -->
<template>
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3">
<ToDoList :items="items" />
</div>
</div>
</div>
</template>
<script>
import ToDoList from '~/components/ToDoList'
import _ from 'lodash'
export default {
components: { ToDoList },
data() {
return {
items: [
{ id: 1, title: 'todo item 1' },
{ id: 2, title: 'todo item 2' },
{ id: 3, title: 'todo item 3' },
{ id: 4, title: 'todo item 4' }
],
finished: [ 4, 2 ]
}
}
}
</script>
页面效果如下:
在 schedule 中,有一个数组 finished,里面存放了已经完成的 item 的 ID,现在需要在已经完成的 item 后打上☑️。
实现如下效果:
解决方法
要实现上述效果,有多个方法,这里采用嵌套 slot 的方式来解决。
主要思路:通过在 slot 上绑定属性, 让 schedule 层可以访问到 ToDoItem 中的 item,然后通过判断 item 是否在 finished 中出现,决定是否将☑️插入到组件 ToDoItem 中。
具体操作如下:
给ToDoItem 添加具名插槽 after,同时将组件中的 item 作为 slot 的一个特性绑定上去:
【将 item 绑定在 slot 上,是为了让父级组件 ToDoList 中的插槽可以访问到该组件中的 item】
<!-- ToDoItem.vue --> <template> <li> <span class="text-muted mr-1">{{ item.id }}.</span> {{ item.title }} <slot name="after" :item="item" /> </li> </template> <script> export default { props: [ 'item' ] } </script>
在 ToDoList 组件中,引用 ToDoItem 的地方,嵌套 slot,修改
ToDoList.vue
如下:<!-- ToDoList.vue --> <template> <ul> <ToDoItem v-for="item in items" :key="item.id" :item="item"> <template v-slot:after="{ item }"> <slot name="todo-item-after" :item="item" /> </template> </ToDoItem> </ul> </template> <script> import ToDoItem from './ToDoItem' export default { components: { ToDoItem }, props: [ 'items' ] } </script>
这里给 ToDoList 添加了一个具名插槽:todo-item-after,同时将 子组件 ToDoItem 中的具名插槽 after 所绑定的特性 item 绑定到 todo-item-after 上,这样外层的 schedule 就可以访问到 ToDoItem 中的 item了。
修改
schedule.vue
,添加函数 isFinished 用于判断是否需要显示☑️:<!-- schedule.vue --> <template> <div class="container"> <div class="row"> <div class="col-md-6 offset-md-3"> <ToDoList :items="items"> <template v-slot:todo-item-after="{ item }"> <i v-if="isFinished(item)" class="fas fa-check" /> </template> </ToDoList> </div> </div> </div> </template> <script> import ToDoList from '~/components/ToDoList' import _ from 'lodash' export default { components: { ToDoList }, data() { return { items: [ { id: 1, title: 'todo item 1' }, { id: 2, title: 'todo item 2' }, { id: 3, title: 'todo item 3' }, { id: 4, title: 'todo item 4' } ], finished: [ 4, 2 ] } }, methods: { isFinished(item) { return _.includes(this.finished, item.id) } } } </script>
如此,便完成了。