树形选择器的使用场景探索

在工作中,树形选择器(tree-select)是一种常见的基础组件, 这次我们针对此组件做一些业务适配

背景:
在一个大型组织(2 万以上)内,需要有一个选择人、组的一个选择器, 支持搜索功能。

image

一开始我的思路是这样:

层级加载+后端搜索

既然数据量很多,那最好不要一次性加载,不然会有以下的一些问题:
问题一是后端的加载速度很慢
二是前端也会手动很大的影响,比如需要开启虚拟列表,选中的值传递、处理、展示需要修改。

所以先定只加载第一层数据,其他数据懒加载,点开时请求后端接口,加载下一层级的数据;关于搜索则需要使用关键字参数来通过接口获取数据,将这些数据全部替换当前的数据。

但是这样设计的话,问题也随之而来:

搜索数据之后选中某个值,之后退出搜索模式,选中的值没有对应的 title,显示成key 了

const [value, setValue] = useState(['0-0-0-1']);

image

针对此种情况,我发动了脑筋,首先想到的解决办法是:

搜索合并

在搜索出结果时,将结果插入原有的数中:

image

这里需要一个算法,即树的合并,同时在原有已有数据的场景下,也要根据 id 去重

在两棵树合并之后,还需要借助 tree-select 自身的前端搜索能力(即开启 optionFilter),将结果筛选出来

这将是一个完整的懒加载+搜索能力的树形选择器了

即使如此,还依旧存在着问题:

问题1:全选和半选的显示

在出现搜索的时候,如果我选中 A 组下的 1 , 那 A 是不是应该是全选,但是组件怎么知道还有 2,3 呢,这时候还没有懒加载出来

image

这种场景下,antd 里并没有类似半选全选的参数,除了魔改源码(通过后端基于参数来知晓是否全选半选)以外暂无其他方案

问题2: 初始值显示

假如通过了全选半选的方案,可以说在保存模式下一件是成功了,但是在编辑的模式下依旧存在着初始值显示的问题,即之前选择的值是 lazy 加载的,但是初始状态下数据源中无值

存储节点名

这时候我想到了官方的 API labelInValue, 于是我尝试了一把:

const [value, setValue] = useState([{id:'0-0-0-1',label:'员工张三'}]);

image

很明显的,他将名称存储到值里确实能够生效,但是也带来了问题:
数据的传递,存储会更吃力

原本只需要 id ,(可能还需要带上 path 路径)的简单数据,现在需要完整的 title,而且变成了对象结构

并且还有一个小问题:

labelInValue 模式下的 label 值,优先级更高与 dataSource 的 value

所以在某个人员的数据变更后(即数据源中的数据更加新),显示的值仍旧是一个旧值

image

忽略这两小问题,可以说选择+回显正常了,不过事实证明我高兴得太早了

接口请求

当前初始值的显示这一切都可以使用接口请求来完成,但是如果层级较深,又或者界面上有多个组件同时存在,那么请求量会瞬间爆炸
并且在请求接口时可能会出现闪烁或加载不及时的情况,体验上也是更差一筹

未知节点

场景:我选中了部门 2,因为是懒加载的,不知道下方有到底多少人,这时候我通过搜索部门 2下方的子员工人员这时候显示的是这样的:

image

假如我取消子员工 2-2 的选中,那么此时我选中的应该是哪些值呢

正确的显示应该是 子员工 2-1,子部门 2-1 …… 等等

当然这里也可以通过接口,在取消选中的时候,再次获取数据,但是实在费力

接口集中获取

回到最初的想法:假如数据可以一次性获取, 那么数据的选择,搜索通过前端自己就能完成,并且初始值的显示也会非常顺利,不过这里就需要后端的一些支持。

结论

  • 懒加载
    • 优势:适合大数据量场景,能减少初始加载时间与资源消耗,支持动态数据更新 ,有较好灵活性与扩展性。
    • 劣势:用户操作可能有延迟感,初始值与搜索处理复杂,开发需管理更多异步逻辑。
  • 一开始请求所有数据
    • 优势:开发简单,数据一致性好,操作即时响应无延迟。
    • 劣势:数据量大时初始加载慢,浪费资源,数据更新困难,可扩展性差。

综上所述,没有绝对更好的方案,应根据具体的业务场景和需求来选择。

posted @ 2025-06-25 20:39  Grewer  阅读(47)  评论(0)    收藏  举报