前几天面试,被问到如何具体开发一个select组件,当时没答好。回来梳理了思路,还是记录一下吧
1.确定外层表单组件触发校验问题
参考ant design 跟ant design vue 几个版本的迭代
文章:
自定义表单控件: 基于vue版本
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