最近完成了一个拖拽分组的需求,在里面用到了作用域插槽,感觉这是一种优秀的组件化方案,所以在此做个记录。


关于Vue插槽的知识,本篇文章不做过多的描述,没有接触过的童靴请移步Vue.js

业务需求

任意多个模型,每个模型中都有若干个字段,字段可在页面上添加,类似于数据库,有多张表且每张表字段任意。现在要求用拖拽的方式给字段分组。

实现

做个拖拽分组需求的小伙伴相比都知道,首先我们要构造一个树形结构

1
2
3
- 模型
-- 分组
--- 字段

刚开始实现的时候,将字段信息写成一个组件Field.vue,而在Group.vue中用一个v-for来处理字段的显示,在index.vue中用v-for处理分组信息的显示。

1
2
3
4
// Group.vue
<div class="field-list-content" v-for="(item, index) in fieldListData" :key="index">
<Field :fieldData="item" />
</div>

按照这种方法也能够实现,但是如果按照这种方法我也不会写这篇博客了,下面我们来看看优化的方案。

优化

通常情况下普通的插槽是父组件使用插槽过程中传入东西决定了插槽的内容。但作用域插槽却可以拿到子组件中的数据
在子组件中创建slot并通过 v-bind 绑定数据 prop 的形式传入数据:

1
<slot :data="data"></slot>

基于作用域插槽的这个特性,将以上代码改写

1
2
3
4
5
6
7
8
9
10
11
12
// index.vue
<field-list
class="group_content"
:fieldListData="item.fieldArray ? item.fieldArray : []"
group="field"
@addGroupItem="onAddGroupItem(item)"
@moveFieldFinish="dealWithFieldGroups"
>
<template v-slot="scope">
<field :fieldData="scope.row" />
</template>
</field-list>

而Group组件内部改造成slot接收来自父组件的字段信息组件,并且将所有逻辑处理代码提升到父组件,实现组件和业务剥离。这也是组件化的精髓。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Group.vue
<draggable :list="fieldListData" v-bind="dragOptions" @end="dealWithFieldGroups">
<div class="field-list-content" v-for="(item, index) in fieldListData" :key="index">
<slot :row="item"></slot>
</div>
<div
style="width: 100%;"
slot="footer"
class="group_detail"
v-if="fieldListData.length === 0"
@click="onAddGroupItem"
>
<el-button type="text" class="add_detail">立即添加</el-button>
</div>
</draggable>

至于字段信息组件,只需要展示即可。

1
2
3
4
5
6
7
// Field.vue
<div class="field_content" :title="fieldData.label">
<icon type="field_info" className="icon"></icon>
<div class="item">
{{ fieldData.label }}
</div>
</div>

最终实现的效果:


本文主要的介绍的是作用域插槽的场景,作用域插槽适合的场景是至少包含三级以上的组件层级,是一种优秀的组件化方案!