Cascader级联选择组件深度实践与常见问题解决方案

程序员雯婷 组件 阅读 1,793
赞 20 收藏
二维码
手机扫码查看
反馈

为啥要搞这么复杂的对比?

最近项目里遇到一个需求,让用户选择省市县三级联动,本来以为很简单,结果发现市面上各种Cascader组件方案五花八门。Element UI的、Ant Design的、还有原生自己写的,每个都有各自的玩法。之前踩过几个坑,这次决定好好梳理一下,免得以后再踩。

Cascader级联选择组件深度实践与常见问题解决方案

说实话,Cascader这个东西看着简单,但实际用起来坑还真不少。数据结构要适配、异步加载要考虑、搜索功能还要支持,一不小心就掉坑里了。

Element UI Cascader:稳妥之选

我比较喜欢用Element UI的Cascader,主要是因为它够成熟,社区支持也到位。代码写起来相对简单:

<template>
  <el-cascader
    v-model="selectedValue"
    :options="options"
    :props="{ 
      value: 'id', 
      label: 'name', 
      children: 'children',
      lazy: true,
      lazyLoad: loadOptions
    }"
    placeholder="请选择地区"
    filterable
  />
</template>

<script>
export default {
  data() {
    return {
      selectedValue: [],
      options: [
        {
          id: 1,
          name: '北京市',
          children: [
            {
              id: 101,
              name: '东城区'
            }
          ]
        }
      ]
    }
  },
  methods: {
    async loadOptions(node, resolve) {
      // 异步加载子选项
      const response = await fetch(https://jztheme.com/api/area/${node.value})
      const data = await response.json()
      resolve(data.map(item => ({
        id: item.id,
        name: item.name,
        leaf: item.level === 3
      })))
    }
  }
}
</script>

Element UI的Cascader有个好处就是配置项比较丰富,支持懒加载、可搜索、多选这些常用的交互。但问题是数据结构有点固定,如果你的后端返回的数据格式不匹配,还得先转换一遍。

Ant Design Cascader:功能更强

Ant Design的Cascader在功能上确实更强大一些,特别是那个动态加载的支持:

import { Cascader } from 'antd';

const App = () => {
  const [options, setOptions] = useState([]);
  
  const loadData = (selectedOptions) => {
    const targetOption = selectedOptions[selectedOptions.length - 1];
    targetOption.loading = true;
    
    fetch(https://jztheme.com/api/area/${targetOption.value})
      .then(response => response.json())
      .then(data => {
        targetOption.loading = false;
        targetOption.children = data.map(item => ({
          label: item.name,
          value: item.id,
          isLeaf: item.level === 3
        }));
        setOptions([...options]);
      });
  };

  return (
    <Cascader
      options={options}
      loadData={loadData}
      changeOnSelect
      placeholder="请选择地区"
      showSearch={{
        filter: (inputValue, path) =>
          path.some(option =>
            option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1
          )
      }}
    />
  );
};

Ant Design的优势在于灵活性更高,特别是那个changeOnSelect属性,可以让用户在每一级都能触发onChange事件。搜索功能也做得比较完善,支持路径搜索。

但这里我踩过一个坑:Ant Design的Cascader对于数据结构的要求更严格,label和value字段不能变,想换个字段名都不行,只能在数据拿到后先处理一遍。

原生手写:完全可控

有时候遇到特别定制的需求,三方库的Cascader不够用,那就得自己写了。虽然麻烦点,但控制力最强:

// 数据处理
function buildTree(data, level = 1) {
  return data.filter(item => item.level === level).map(item => ({
    ...item,
    children: buildTree(data, level + 1).filter(child => child.parentId === item.id)
  }));
}

// 组件实现
export default {
  data() {
    return {
      panels: [[], [], []],
      selected: []
    }
  },
  mounted() {
    this.loadAreaData();
  },
  methods: {
    async loadAreaData(level = 1, parentId = null) {
      const params = parentId ? ?parentId=${parentId} : ?level=${level};
      const response = await fetch(https://jztheme.com/api/area${params});
      const data = await response.json();
      
      if (level <= 3) {
        this.$set(this.panels, level - 1, data);
      }
    },
    onPanelChange(level, item) {
      this.selected[level] = item;
      this.selected.splice(level + 1); // 清空后面的选择
      
      if (level < 2) { // 最多三级
        this.loadAreaData(level + 2, item.id);
      }
    }
  }
}

手写的好处就是完全按照你的业务逻辑来,想要什么功能自己加。比如我现在做的项目里,需要在第二级选择后立即触发某些业务逻辑,这种情况三方库都很难支持,只能自己实现。

但缺点也很明显:工作量大,要考虑的边界情况多,而且还要自己处理动画效果、键盘导航这些用户体验相关的细节。

谁更灵活?谁更省事?

从我的实际使用经验来看,如果是一般的业务需求,Element UI是最省事的,配置项够用,文档也比较清晰。遇到复杂一点的交互逻辑,Ant Design会更灵活一些。

但如果真的遇到特别定制的需求,比如需要在每一级显示额外的信息、特殊的交互逻辑等等,还是得考虑自己写。虽然费劲,但能完全掌控。

性能方面其实差别不大,都是按需加载,主要是数据处理这块需要注意。我之前在一个项目里没处理好数据缓存,每次打开都重新请求,用户体验就很差。

我的选型逻辑

现在我一般是这样选的:

  • 简单的需求,Element UI就够了,开发速度快
  • 需要特殊交互或者功能要求比较多,优先考虑Ant Design
  • 特别定制化的需求,或者是性能要求很高的场景,考虑手写

还有一个考虑因素就是团队成员的熟悉程度。如果大家都用惯了Element UI,就没必要为了一个小功能切换到别的库。

数据结构的适配也是个头疼的问题。我个人建议在项目初期就把后端返回的数据格式统一规范一下,避免前端做过多的数据转换工作。

踩坑提醒:几个常见陷阱

用了这么久的Cascader,踩过几个印象深刻的坑:

首先是懒加载的时候,如果异步请求失败了,一定要给用户明确的错误提示,不然界面看起来正常,实际上数据加载不出来,用户体验很糟糕。

还有就是搜索功能,有些用户会输入很奇怪的字符,记得做好防抖和异常处理,别因为用户的输入导致页面崩溃。

最后一个就是移动端兼容性,某些老版本的移动端浏览器对Cascader的滚动支持不太友好,需要额外处理。

以上是我个人对这个Cascader组件的完整讲解,有更优的实现方式欢迎评论区交流。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论