引言:重复代码的困境
在前端开发中,数据筛选是一个高频需求。当面对类似 filterNameA、filterNameB、filterNameC 等多字段筛选时,许多开发者会本能地写出重复的条件分支。这虽然功能可行,却隐藏着维护性差、可读性低、性能隐患等问题。
本文将通过一个真实案例,展示如何通过策略模式、函数式编程和逻辑抽象,将冗余代码转化为简洁高效的解决方案。
优化前的代码
js
// ...
for (const i in filterParams) {
if (filterParams[i] !== undefined) {
if (i === 'filterNameA') {
listData = listData.filter(item =>
filterParams[i].includes(item?.filterNameA || '')
)
}
if (i === 'filterNameB') {
listData = listData.filter(item =>
filterParams[i].includes(item?.filterNameB || '')
)
}
if (i === 'filterNameC') {
listData = listData.filter(item =>
filterParams[i].includes(item?.filterNameC || '')
)
}
if (i === 'filterNameD') {
listData = listData.filter(item =>
filterParams[i].includes(item?.filterNameD || '') || item?.items?.find(child => filterParams[i].includes(child?.filterNameD || ''))
)
}
if (i === 'filterNameE') {
listData = listData.filter(item =>
filterParams[i].includes(item?.filterNameE || '')
)
}
}
}
// ...
原始代码的三大痛点
- 重复劳动:每个字段的 if 分支几乎相同,仅字段名不同
- 硬编码耦合:字段名直接写入逻辑,修改需求时必须改动核心代码
- 性能损耗:链式 filter 导致多次遍历数据,时间复杂度 O(n*m)
优化四部曲
- 策略模式解耦
js
const fieldFilters = { fieldName: (item, value) => /* 过滤逻辑 */ }
将每个字段的过滤逻辑封装为独立函数,消除重复分支。
- 单次遍历优化
使用Object.entries + every
组合,在一次遍历中完成所有条件判断:
js
list.filter(item => Object.entries(filterParams).every(([key, value]) => /*...*/) )
- 防御性编程
空值合并运算符 ??
统一处理 undefined/null
可选链 ?.
避免深层属性访问报错
- 特殊逻辑封装
对于 filterNameD
等复杂场景,独立函数保持主逻辑简洁:
js
const childMatch = item?.items?.some(child => /*...*/)
优化后的代码
js
/**
* 表头筛选选项
* @param {Array} listData 表格源数据
* @param {Object} filterParams 已选择表头筛选项
* @returns {Array} 筛选后的表格源数据
*/
export function filterListData(listData, filterParams) {
if (!filterParams || !listData) {
return listData
}
// 定义字段映射和过滤逻辑
const fieldFilters = {
filterNameA: (item, value) => value?.includes(item?.filterNameA ?? ''),
filterNameB: (item, value) => value?.includes(item?.filterNameB ?? ''),
filterNameC: (item, value) => value?.includes(item?.filterNameC ?? ''),
filterNameD: (item, value) => {
// 检查主项或子项中是否包含指定的筛选值
const mainItemMatch = value?.includes(item?.filterNameD)
const childItemMatch =
item?.items?.length &&
item.items.some(child => value?.includes(child.filterNameD))
return mainItemMatch || childItemMatch
},
filterNameE: (item, value) => value?.includes(item?.filterNameE ?? ''),
}
return listData.filter(item => {
// 检查所有筛选条件
return Object.entries(filterParams).every(([key, value]) => {
// 如果值为 undefined 或 空字符串 或 为空数组,跳过该筛选条件
if (
value === undefined ||
value === null ||
value === '' ||
(Array.isArray(value) && value.length === 0)
) {
return true
}
const filterFn = fieldFilters[key]
return filterFn ? filterFn(item, value) : true
})
})
}
优化效果对比
指标 | 优化前 | 优化后 |
---|---|---|
时间复杂度 | O(n*m) | O(n) |
可扩展性 | 需修改主逻辑 | 仅增删映射表 |
延伸思考
- 动态配置:将
fieldFilters
提取为参数,支持运行时注册新过滤规则 - 性能监控:对大数据集添加
console.time
量化优化收益 - 单元测试:每个过滤函数应具备独立测试用例
结语:优雅代码的哲学
最好的代码不是能运行的代码,而是像散文一样可读、像数学一样精确、像乐高一样可组合的代码。本次优化不仅减少了 80%的重复代码,更构建了一个易于维护的过滤框架------这正是前端工程化的魅力所在。
如果这篇文章帮到了你,欢迎点赞👍和关注⭐️。
文章如有错误之处,希望在评论区指正🙏🙏