Ant Design TreeSelect 树选择控件的二次封装及原理分析 - zptime/blog GitHub Wiki

类似于 select,可选数据结构是一个树形结构,比如公司层级、省市区等,我举例的是公司的部门树架构。

Antd TreeSelect 官方文档:https://antdv.com/components/tree-select-cn/

主要对 Antd TreeSelect 树型选择控件进行了二次封装,而且解释了一些封装的基础知识,如属性vm.$attrs, 双向绑定model,监听器vm.$listeners

封装原因:之前部门树数据是一次性加载的, 存在着性能问题,数据量很大的时候,加载很缓慢,而且组件可能崩溃;因此要改成异步加载数据,但是系统中用到的地方很多,不可能一个地方一个地方的改,所以封装一下,统一处理。

基础使用

  1. 开启搜索框,设置showSearch
  2. 过滤搜索问题:要结合treeNodeFilterProp使用,默认是 value,一般需要改成 title
  3. 滚动定位问题:默认是跟随 body 定位的,滚动时会出现错位现象,需要设置getPopupContainer处理
  4. 下拉框样式设置:可以用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" />

封装原理

属性$attrs

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

model 官方文档:https://cn.vuejs.org/v2/api/#model

官方解释:允许一个自定义组件在使用 v-model 时,定制 propevent。默认情况下,一个组件上的 v-model 会把 value 用作 prop, 且把 input 用作 event

实例解释:如上所示,我定义的是 valueselect;如果要改成 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>

监听器$listeners

官方文档: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>
⚠️ **GitHub.com Fallback** ⚠️