实现select搜索选择组件

491 阅读1分钟

前几天面试,被问到如何具体开发一个select组件,当时没答好。回来梳理了思路,还是记录一下吧

1.确定外层表单组件触发校验问题

参考ant design 跟ant design vue 几个版本的迭代

文章:

自定义表单控件: 基于vue版本 WX20230206-171750@2x.png

2.确定组件功能

  • 外部传入api url,可进行带参搜索;
  • placeholder/size/style/value 属性传递;
  • 组成Option的key,value值配置传入;
  • 搜索防抖:用 debounce 函数来避免大量重复请求;
  • alwaysLoad,每一次加载都重新获取数据
  • 是否懒加载
  • 可自定义样式拓展

3.实现

定义可配置项
props: {
      value: [Array, Object, String, Number],
      numberToString: propTypes.bool,
      api: {
        type: Function as PropType<(arg?: Recordable) => Promise<OptionsItem[]>>,
        default: null,
      },
      // api params
      params: {
        type: Object as PropType<Recordable>,
        default: () => ({}),
      },
      preParams: {
        type: Object as PropType<Recordable>,
        default: () => ({}),
      },
      // support xxx.xxx.xx
      resultField: propTypes.string.def(''),
      labelField: propTypes.string.def('label'),
      valueField: propTypes.string.def('value'),
      immediate: propTypes.bool.def(true),
      alwaysLoad: propTypes.bool.def(false),
      isLazy: propTypes.bool.def(false),
    },
根据api请求数据
      async function fetch(currentPageNum?: Number) {
        const api = props.api;
        if (!api || !isFunction(api)) return;
        if (!currentPageNum) {
          options.value = [];
          preOptions.value = [];
          currentPage.value = 1;
        }
        try {
          loading.value = true;
          // 增加懒加载当前页参数
          const fetchParams =
            props.isLazy && currentPageNum
              ? { ...props.params, pageNum: currentPageNum }
              : props.params;
          if (props.isLazy && props.preParams && !currentPageNum && props.resultField) {
            if (Object.values(props.preParams).filter((_) => _).length > 0) {
              const preRes = await api({ ...fetchParams, ...props.preParams, pageSize: 1000 });
              preOptions.value = [...get(preRes, props.resultField)];
              options.value = [...get(preRes, props.resultField)];
            }
          }
          const res = await api(fetchParams);
          totalCount.value = Number(get(res, 'total')) || 0;
          if (Array.isArray(res)) {
            options.value = [...options.value, ...res];
            emitChange();
            return;
          }
          if (props.resultField) {
            options.value = [...options.value, ...get(res, props.resultField)] || [];
          }
          emitChange();
        } catch (error) {
          console.warn(error);
        } finally {
          loading.value = false;
        }
      }
格式化获取Option
  const getOptions = computed(() => {
        const { labelField, valueField, numberToString } = props;

        return unref(options).reduce((prev, next: Recordable) => {
          if (next) {
            const value = next[valueField];
            prev.push({
              ...omit(next, [labelField, valueField]),
              label: next[labelField],
              value: numberToString ? `${value}` : value,
            });
          }
          return prev;
        }, [] as OptionsItem[]);
      });

4.拓展

组件库的建立

参考:每个前端都值得拥有自己的组件库,就像每个夏天都拥有西瓜🍉

  • 开发者仓库管理:用基于lerna的多包管理工具,规范化配置
  • 组件设计:learn单包管理
  • 单元测试:jest
  • 持续集成:CICD配置
  • 协作管理:git