在工作中,樹形選擇器(tree-select)是一種常見的基礎組件, 這次我們針對此組件做一些業務適配
背景:
在一個大型組織(2 萬以上)內,需要有一個選擇人、組的一個選擇器, 支持搜索功能。
一開始我的思路是這樣:
層級加載+後端搜索
**既然數據量很多,那最好不要一次性加載,不然會有以下的一些問題:
問題一是後端的加載速度很慢
二是前端也會手動很大的影響,比如需要開啓虛擬列表,選中的值傳遞、處理、展示需要修改。**
所以先定只加載第一層數據,其他數據懶加載,點開時請求後端接口,加載下一層級的數據;關於搜索則需要使用關鍵字參數來通過接口獲取數據,將這些數據全部替換當前的數據。
但是這樣設計的話,問題也隨之而來:
在搜索數據之後選中某個值,之後退出搜索模式,選中的值沒有對應的 title,顯示成key 了
const [value, setValue] = useState(['0-0-0-1']);
針對此種情況,我發動了腦筋,首先想到的解決辦法是:
搜索合併
在搜索出結果時,將結果插入原有的數中:
這裏需要一個算法,即樹的合併,同時在原有已有數據的場景下,也要根據 id 去重
在兩棵樹合併之後,還需要藉助 tree-select 自身的前端搜索能力(即開啓 optionFilter),將結果篩選出來
這將是一個完整的懶加載+搜索能力的樹形選擇器了
即使如此,還依舊存在着問題:
問題1:全選和半選的顯示
在出現搜索的時候,如果我選中 A 組下的 1 , 那 A 是不是應該是全選,但是組件怎麼知道還有 2,3 呢,這時候還沒有懶加載出來
這種場景下,antd 裏並沒有類似半選全選的參數,除了魔改源碼(通過後端基於參數來知曉是否全選半選)以外暫無其他方案
問題2: 初始值顯示
假如通過了全選半選的方案,可以説在保存模式下一件是成功了,但是在編輯的模式下依舊存在着初始值顯示的問題,即之前選擇的值是 lazy 加載的,但是初始狀態下數據源中無值
存儲節點名
這時候我想到了官方的 API labelInValue, 於是我嘗試了一把:
const [value, setValue] = useState([{id:'0-0-0-1',label:'員工張三'}]);
很明顯的,他將名稱存儲到值裏確實能夠生效,但是也帶來了問題:
數據的傳遞,存儲會更吃力
原本只需要 id ,(可能還需要帶上 path 路徑)的簡單數據,現在需要完整的 title,而且變成了對象結構
並且還有一個小問題:
labelInValue 模式下的 label 值,優先級更高與 dataSource 的 value
所以在某個人員的數據變更後(即數據源中的數據更加新),顯示的值仍舊是一箇舊值
忽略這兩小問題,可以説選擇+回顯正常了,不過事實證明我高興得太早了
接口請求
當前初始值的顯示這一切都可以使用接口請求來完成,但是如果層級較深,又或者界面上有多個組件同時存在,那麼請求量會瞬間爆炸
並且在請求接口時可能會出現閃爍或加載不及時的情況,體驗上也是更差一籌
未知節點
場景:我選中了部門 2,因為是懶加載的,不知道下方有到底多少人,這時候我通過搜索部門 2下方的子員工人員這時候顯示的是這樣的:
假如我取消子員工 2-2 的選中,那麼此時我選中的應該是哪些值呢
正確的顯示應該是 子員工 2-1,子部門 2-1 …… 等等
當然這裏也可以通過接口,在取消選中的時候,再次獲取數據,但是實在費力
接口集中獲取
回到最初的想法:假如數據可以一次性獲取, 那麼數據的選擇,搜索通過前端自己就能完成,並且初始值的顯示也會非常順利,不過這裏就需要後端的一些支持。
結論
-
懶加載:
- 優勢:適合大數據量場景,能減少初始加載時間與資源消耗,支持動態數據更新 ,有較好靈活性與擴展性。
- 劣勢:用户操作可能有延遲感,初始值與搜索處理複雜,開發需管理更多異步邏輯。
-
一開始請求所有數據:
- 優勢:開發簡單,數據一致性好,操作即時響應無延遲。
- 劣勢:數據量大時初始加載慢,浪費資源,數據更新困難,可擴展性差。
綜上所述,沒有絕對更好的方案,應根據具體的業務場景和需求來選擇。