Ant Design TreeSelect 树选择控件的二次封装及原理分析 - zptime/blog GitHub Wiki
类似于 select,可选数据结构是一个树形结构,比如公司层级、省市区等,我举例的是公司的部门树架构。
Antd TreeSelect 官方文档:https://antdv.com/components/tree-select-cn/
主要对 Antd TreeSelect 树型选择控件进行了二次封装,而且解释了一些封装的基础知识,如属性
vm.$attrs
, 双向绑定model
,监听器vm.$listeners
等
封装原因:之前部门树数据是一次性加载的, 存在着性能问题,数据量很大的时候,加载很缓慢,而且组件可能崩溃;因此要改成异步加载数据,但是系统中用到的地方很多,不可能一个地方一个地方的改,所以封装一下,统一处理。
- 开启搜索框,设置
showSearch
; - 过滤搜索问题:要结合
treeNodeFilterProp
使用,默认是value
,一般需要改成title
- 滚动定位问题:默认是跟随 body 定位的,滚动时会出现错位现象,需要设置
getPopupContainer
处理 - 下拉框样式设置:可以用
dropdownStyle
,注意以对象形式传值
<a-tree-select
ref="departIds"
placeholder="请选择"
searchPlaceholder="请输入部门名称"
:treeDefaultExpandAll="true"
v-model="departIds"
:treeData="departs"
:dropdownStyle="{ maxHeight: '400px' }"
show-search
treeNodeFilterProp="title"
:getPopupContainer="(triggerNode) => triggerNode.parentNode"
>
</a-tree-select>
实现效果展示:
只是进行一个简单的封装,统一处理一些属性,没必要每次都写那么多一样的东西。 在一个管理后台系统中,组件使用大体上是差不多的,封装一下使用更优雅,对于以后扩展更方便一点,可以统一处理,没必要每个地方都改一下。
(1)子组件 DepartTreeSelect.vue
<template>
<a-tree-select
v-bind="$attrs"
placeholder="请选择"
searchPlaceholder="请输入部门名称"
:treeDefaultExpandAll="true"
:treeData="departSource"
:dropdownStyle="{ maxHeight: '400px' }"
show-search
treeNodeFilterProp="title"
:getPopupContainer="(triggerNode) => triggerNode.parentNode"
@select="handleSelect"
>
</a-tree-select>
</template>
<script>
export default {
props: {
departSource: {
type: Array,
default: [],
},
},
model: {
prop: "value",
event: "select",
},
methods: {
handleSelect(selectedKeys) {
this.$emit("change", selectedKeys);
},
},
};
</script>
<style lang="scss" scoped></style>
(2)父组件调用
<departTreeSelect :depart-source="departs" v-model="departIds" />
vm.$attrs
官方文档:https://cn.vuejs.org/v2/api/#vm-attrs
官方解释:包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)
通用解释:父组件中形如:foo="xxx"
或者 v-bind="{value: 12}"
等传给子组件的属性,但凡没有被子组件props
接收的,都会放到子组件的$attrs
中;被子组件props
接收的,会放到子组件的$props
中
实例解释:如上代码中,子组件中加了v-bind="$attrs"
后,props
接收了 departSource
,那么$attrs
打印出来只有{value: ""}
;如果不接收departSource
,$attrs
打印出来就是{value: "", departSource: ""}
,那么treeData
的取值方式也要改成:treeData="$attrs.departSource"
model 官方文档:https://cn.vuejs.org/v2/api/#model
官方解释:允许一个自定义组件在使用 v-model
时,定制 prop
和 event
。默认情况下,一个组件上的 v-model
会把 value
用作 prop
, 且把 input
用作 event
。
实例解释:如上所示,我定义的是 value
和 select
;如果要改成 departId 和 change,父组件传值要通过:departId="departIds"
传值,而且子组件获取值也要改变,要通过v-model="$attrs.departId"
绑定一下,如下所示:
// 父组件调用
<departTreeSelect :departSource="departments" :departId="baseInfo.departIds" />
// 子组件
<template>
<a-tree-select
v-bind="$attrs"
v-model="$attrs.departId"
:treeData="$attrs.departSource"
>
</a-tree-select>
</template>
<script>
export default {
model: {
prop: "departId",
event: "change",
},
methods: {
handleSelect(selectedKeys) {
// 回调要和`model.event`对应
this.$emit("change", selectedKeys);
},
},
};
</script>
官方文档:https://cn.vuejs.org/v2/api/#vm-listeners
官方解释:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。
通用解释:父组件以 @eventName="fn"
或者 v-on:eventName="fn"
对子组件挂载事件监听。对子组件而言,父组件监听的事件都放在$listeners
里。
实例解释:之前双向绑定中,事件的回调用的是@select=handleSelect
,可以换成如下的方法,原理是一样的。
<template>
<a-tree-select v-on="treeListeners"> </a-tree-select>
</template>
<script>
export default {
computed: {
treeListeners() {
var vm = this;
return Object.assign(
// 挂载父组件所有对自己的事件监听
this.$listeners,
// 添加自定义监听器,或覆写一些监听器的行为
{
// 确保组件配合 `v-model` 的工作
select: function(event) {
vm.$emit("select", event);
},
}
);
},
},
};
</script>