Vue3 组合式 API 在大型项目中的实践:useOperatorData 封装与应用
前言
在大型前端项目开发中,代码复用和逻辑解耦一直是我们追求的目标。Vue3 的组合式 API 为我们提供了一种全新的思路,让我们能够将相关逻辑代码组织在一起,形成可复用的"组合函数"。本文将通过分析 useOperatorData
这个实际案例,探讨组合式 API 在业务开发中的应用与优势。
问题背景
在我们的项目中,几乎所有接口都需要传递运营商参数,这导致了大量重复代码:
// 传统写法:每个组件都需要重复处理运营商参数
const getData = async () => {
const res = await Api.someApi({
category: '3',
operatorIdSet: operatorTypeStore.getCurrentOperatorIdSet()
// 其他参数...
});
// 处理数据...
};
这种写法存在几个问题:
- 代码重复:每个组件都需要编写相似的参数处理逻辑
- 耦合性高:业务组件直接依赖 store 的具体实现
- 维护困难:如果参数处理逻辑需要修改,需要修改所有组件
我们的解决方案:useOperatorData
我们通过组合式 API 实现了一个简单而实用的解决方案:
// src/hooks/useOperatorData.ts
import { useOperatorTypeStore } from '@/store/modules/operator-type';
export function useOperatorData() {
const operatorTypeStore = useOperatorTypeStore();
// 获取处理后的请求参数
const getParams = (params = {}) => {
return {
...params,
operatorIdSet: operatorTypeStore.getCurrentOperatorIdSet()
};
};
// 监听运营商变化
const onOperatorChange = (callback) => {
operatorTypeStore.$subscribe((mutation, state) => {
callback();
});
};
return {
getParams,
onOperatorChange
};
}
实际应用:
// 在组件中使用
import { useOperatorData } from '@/hooks/useOperatorData';
const { getParams } = useOperatorData();
const getData = async () => {
const params = getParams({ category: '3' });
const res = await Api.someApi(params);
// 处理数据...
};
优势分析
1. 高内聚,低耦合
组合式 API 的设计理念是"关注点分离"。useOperatorData
将"获取运营商参数"这一关注点完全封装起来,业务组件不需要关心具体实现。
2. 逻辑复用,减少重复代码
在项目中,我们只需要在组件中导入并使用这个 hook,就能轻松处理运营商参数:
import { useOperatorData } from '@/hooks/useOperatorData';
const { getParams } = useOperatorData();
3. 与生命周期、状态管理无缝集成
这是组合式 API 最强大的特性之一。我们的 useOperatorData
不仅提供了参数处理,还与 Pinia 状态变化关联起来:
// 在组件中使用
const { getParams, onOperatorChange } = useOperatorData();
onMounted(() => {
getData();
});
// 当运营商选择变化时,自动刷新数据
onOperatorChange(getData);
实际应用案例
在我们的项目中,这种改造非常简单,只需少量代码修改:
// 修改前
const getData = async () => {
isLoading.value = true;
try {
const res = await Api.findStationUtilizationRate({
timeRange: activeTime.value,
category: '3'
});
processRankData(res);
} catch (error) {
} finally {
isLoading.value = false;
}
};
// 修改后
import { useOperatorData } from '@/hooks/useOperatorData';
const { getParams } = useOperatorData();
const getData = async () => {
isLoading.value = true;
try {
const params = getParams({
timeRange: activeTime.value,
category: '3'
});
const res = await Api.findStationUtilizationRate(params);
processRankData(res);
} catch (error) {
} finally {
isLoading.value = false;
}
};
更进一步:全局筛选条件的高内聚实现
在大型数据可视化项目中,除了运营商筛选,我们还经常遇到其他全局筛选条件:日期范围、区域筛选、数据维度等。组合式 API 为这些场景提供了理想的解决方案。
多维筛选条件的组合实现
我们可以将不同的筛选逻辑封装成独立的组合函数,再组合使用:
// 时间范围筛选
function useTimeRange() {
const timeStore = useTimeStore();
const getTimeParams = (params = {}) => {
return {
...params,
startTime: timeStore.startTime,
endTime: timeStore.endTime
};
};
const onTimeChange = (callback) => {
timeStore.$subscribe(() => callback());
};
return { getTimeParams, onTimeChange };
}
// 区域筛选
function useAreaFilter() {
const areaStore = useAreaStore();
const getAreaParams = (params = {}) => {
return {
...params,
areaCode: areaStore.selectedAreaCode
};
};
const onAreaChange = (callback) => {
areaStore.$subscribe(() => callback());
};
return { getAreaParams, onAreaChange };
}
// 组合使用 - 高级组合函数
function useCompleteFilter() {
const { getParams: getOperatorParams, onOperatorChange } = useOperatorData();
const { getTimeParams, onTimeChange } = useTimeRange();
const { getAreaParams, onAreaChange } = useAreaFilter();
// 组合所有参数
const getAllParams = (baseParams = {}) => {
return getAreaParams(getTimeParams(getOperatorParams(baseParams)));
};
// 组合所有变化监听
const onAnyFilterChange = (callback) => {
onOperatorChange(callback);
onTimeChange(callback);
onAreaChange(callback);
};
return { getAllParams, onAnyFilterChange };
}
在组件中的应用
// 在复杂的数据分析组件中
import { useCompleteFilter } from '@/hooks/useCompleteFilter';
const { getAllParams, onAnyFilterChange } = useCompleteFilter();
const loadDashboardData = async () => {
isLoading.value = true;
try {
// 一行代码处理所有筛选条件
const params = getAllParams({ module: 'dashboard' });
const results = await Promise.all([
Api.getOverviewData(params),
Api.getTrendData(params),
Api.getDistributionData(params)
]);
// 处理数据...
} finally {
isLoading.value = false;
}
};
onMounted(loadDashboardData);
// 任何筛选条件变化都自动刷新数据
onAnyFilterChange(loadDashboardData);
响应式状态与生命周期的完美融合
这种模式的强大之处在于将响应式状态、生命周期和状态管理完美融合:
- 状态隔离:每个筛选逻辑封装在独立的组合函数中,不互相干扰
- 自动响应:通过 Pinia 的订阅机制,任何状态变化都能触发数据刷新
- 逻辑清晰:组件只需关注如何使用数据,不需要关心筛选条件的实现细节
- 灵活组合:可以根据需要组合不同的筛选条件,灵活性极高
适用场景与最佳实践
基于组合式 API 的筛选条件封装,特别适合以下场景:
- 复杂的数据分析系统:需要多维度筛选的数据可视化平台
- 全局筛选条件:如日期范围、区域、部门、分类等需要全局共享的筛选条件
- 条件之间有依赖关系:例如选择省份后需要联动刷新城市列表
最佳实践:
- 单一职责原则:每个组合函数应只负责一种筛选逻辑
- 组合而非继承:通过组合多个基础组合函数来实现复杂功能
- 显式依赖:明确声明组合函数的依赖,避免隐式依赖
- 命名规范:使用统一的命名规范,如 getXXXParams, onXXXChange 等
- 测试友好:设计时考虑单元测试的便利性
结语
Vue3 的组合式 API 为我们提供了组织代码的新思路,特别适合处理全局筛选、状态共享等高复用场景。通过将筛选逻辑与业务逻辑分离,我们不仅提高了代码的可维护性,也极大地提升了开发效率。
useOperatorData
只是我们实践的开始,基于这一思路,我们可以构建出更加复杂、灵活的组合函数体系,最终实现高内聚、低耦合的理想代码架构。在大型项目中,这种模式已经为我们带来了显著的效益,相信对于其他团队也会有所启发。