【路径查询组件优化记录:数据处理与显示逻辑重构】

路径查询组件优化记录:数据处理与显示逻辑重构

一、优化背景与目标

在路径查询系统中,station-display.vue 组件负责展示查询结果,包括线路名称、站点信息和换乘方案等关键信息。通过代码审查,发现了两个需要优化的问题:

  1. 线路名称显示错误:连接线上显示的线路名称取自下一个站点而非当前站点
  2. 数据处理分散:数据字段处理逻辑分散在多处,导致维护困难

优化目标是在不影响现有功能的前提下,修正显示错误并提高代码的可维护性。

二、具体改动点与前后对比

1. 线路名称显示逻辑修正

问题:在站点连接线上,线路名称错误地显示了下一个站点的线路,而非当前站点的线路。

修改前代码

vue 复制代码
<template v-for="(station, idx) in path.stations" :key="'conn-' + idx">
    <div v-if="idx < path.stations.length - 1" class="station-connection" :style="{
        left: `calc(${(idx + 0.5) * 100 / (path.stations.length - 1)}%)`,
        transform: 'translateX(-50%)'
    }">
        <span class="line-station-info">
            {{ path.stations[idx + 1].lineName ? path.stations[idx + 1].lineName.replace('地铁', '') : '' }}/{{
                path.stations[idx + 1].stationNum || 0 }}站
        </span>
    </div>
</template>

修改后代码

vue 复制代码
<template v-for="(station, idx) in path.stations" :key="'conn-' + idx">
    <div v-if="idx < path.stations.length - 1" class="station-connection" :style="{
        left: `calc(${(idx + 0.5) * 100 / (path.stations.length - 1)}%)`,
        transform: 'translateX(-50%)'
    }">
        <span class="line-station-info">
            {{ path.stations[idx].lineName ? path.stations[idx].lineName.replace('地铁', '') : '' }}/{{
                path.stations[idx + 1].stationNum || 0 }}站
        </span>
    </div>
</template>

效果对比

  • 修改前:连接线显示的是下一个站点的线路名称,例如从1号线到2号线的连接线会显示"2号线/3站"
  • 修改后:连接线显示的是当前站点的线路名称,例如从1号线到2号线的连接线会显示"1号线/3站"

视觉变化

用户现在能看到正确的线路信息,特别是在换乘点,能清晰地知道当前所在的线路,而不是错误地显示即将换乘的线路。

2. 数据处理逻辑集中化

a. 总站数显示字段统一

修改前代码

vue 复制代码
<div class="line-time-station">
    <span class="line-name">{{ path.lineName || '暂无线路名称' }}</span>
    <span class="time-station" v-if="path.duration">{{
        secondToMinute(path.duration) }}分钟/{{ path.allNum || 0
        }}站</span>
</div>

修改后代码

vue 复制代码
<div class="line-time-station">
    <span class="line-name">{{ path.lineName || '暂无线路名称' }}</span>
    <span class="time-station" v-if="path.duration">{{
        secondToMinute(path.duration) }}分钟/{{ path.totalStations || 0
        }}站</span>
</div>

效果对比

  • 修改前 :直接使用API返回的allNum字段
  • 修改后 :使用统一处理后的totalStations字段,但值仍来自API的allNum
b. 数据处理函数重构

修改前代码

javascript 复制代码
// 处理站点数据
const processStations = (path) => {
    const stations = [];

    if (path.schedulesList && path.schedulesList.length > 0) {
        // 添加起始站
        stations.push({
            stationType: '0',
            stationName: path.originStationName || path.schedulesList[0]?.stationName || '',
            lineName: `${path.schedulesList[0]?.lineId || ''}号线`,
            stationNum: path.schedulesList[0]?.stationNum || 0
        });

        // 添加中间站/换乘站
        for (let i = 1; i < path.schedulesList.length; i++) {
            const station = path.schedulesList[i];
            const isLastStation = i === path.schedulesList.length - 1;

            stations.push({
                stationType: isLastStation ? '1' : '2',
                stationName: isLastStation ?
                    (path.destStationName || station.stationName || '') :
                    (station.stationName || ''),
                lineName: `${station.lineId || ''}号线`,
                stationNum: station.stationNum || 0
            });
        }
    } else if (path.originStationName && path.destStationName) {
        // 只有起终点信息的情况
        stations.push({
            stationType: '0',
            stationName: path.originStationName,
            lineName: `${path.originLineId || ''}号线`,
            stationNum: 0
        });

        stations.push({
            stationType: '1',
            stationName: path.destStationName,
            lineName: `${path.endLineId || ''}号线`,
            stationNum: 0
        });
    }

    return stations;
};

// 查询按钮点击处理
const handleQuery = async () => {
    // ... 验证代码 ...
    try {
        // 调用API查询路径
        const response = await api.findOdPath(params);

        if (response && Array.isArray(response) && response.length > 0) {
            pathResults.value = response.map(path => ({
                programmeType: path.simpleType || 0,
                programmeIndex: path.programmeIndex || 1,
                lineName: path.lineName || `${path.endLineId || ''}号线 (${path.originStationName || ''} - ${path.destStationName || ''})`,
                duration: path.travelTime || 0,
                stationNum: path.stopList?.length || 0,
                transferNum: path.transferNum || 0,
                stations: processStations(path),
                nameScope: path.nameScope || '',
                simpleType: path.simpleType || '0',
                schedulesList: path.schedulesList || [],
                allNum: path.allNum || 0
            }));
            // ... 选择路径代码 ...
        }
    } catch (error) {
        // ... 错误处理 ...
    }
};

// watch中的数据处理
watch(() => props.selectedPath, (newPath) => {
    // ... 其他代码 ...
    if (newPath) {
        try {
            // ... 线路和站点处理代码 ...
            // 更新路径结果
            pathResults.value = [{
                programmeType: newPath.programmeType || 0,
                programmeIndex: newPath.programmeIndex || 1,
                lineName: newPath.lineName || '',
                duration: newPath.duration || 0,
                stationNum: newPath.stationNum || 0,
                transferNum: newPath.transferNum || 0,
                stations: newPath.stations || [],
                nameScope: newPath.nameScope || '',
                simpleType: newPath.simpleType || '0',
                schedulesList: newPath.schedulesList || []
            }];
            selectedPathIndex.value = 0;
        } catch (error) {
            // ... 错误处理 ...
        }
    }
}, { immediate: true, deep: true });

修改后代码

javascript 复制代码
// 数据处理工具函数
/**
 * 处理路径数据,统一数据格式
 * @param {Object} rawPath - 原始路径数据
 * @returns {Object} - 处理后的路径数据
 */
const processPathData = (rawPath) => {
    // 处理站点数据
    const stations = processStations(rawPath);
    
    return {
        programmeType: rawPath.simpleType || 0,
        programmeIndex: rawPath.programmeIndex || 1,
        lineName: rawPath.lineName || `${rawPath.endLineId || ''}号线 (${rawPath.originStationName || ''} - ${rawPath.destStationName || ''})`,
        duration: rawPath.travelTime || 0,
        stationNum: rawPath.stopList?.length || 0,
        transferNum: rawPath.transferNum || 0,
        stations: stations,
        nameScope: rawPath.nameScope || '',
        simpleType: rawPath.simpleType || '0',
        schedulesList: rawPath.schedulesList || [],
        totalStations: rawPath.allNum || 0 // 直接使用接口返回的allNum
    };
};

/**
 * 处理站点数据,统一数据格式
 * @param {Object} path - 路径数据
 * @returns {Array} - 处理后的站点数组
 */
const processStations = (path) => {
    const stations = [];

    if (path.schedulesList && path.schedulesList.length > 0) {
        // 添加起始站
        stations.push(createStationData({
            stationType: '0',
            stationName: path.originStationName || path.schedulesList[0]?.stationName || '',
            lineId: path.schedulesList[0]?.lineId || '',
            stationNum: path.schedulesList[0]?.stationNum || 0
        }));

        // 添加中间站/换乘站
        for (let i = 1; i < path.schedulesList.length; i++) {
            const station = path.schedulesList[i];
            const isLastStation = i === path.schedulesList.length - 1;

            stations.push(createStationData({
                stationType: isLastStation ? '1' : '2',
                stationName: isLastStation ? (path.destStationName || station.stationName || '') : (station.stationName || ''),
                lineId: station.lineId || '',
                stationNum: station.stationNum || 0
            }));
        }
    } else if (path.originStationName && path.destStationName) {
        // 只有起终点信息的情况
        stations.push(createStationData({
            stationType: '0',
            stationName: path.originStationName,
            lineId: path.originLineId || '',
            stationNum: 0
        }));

        stations.push(createStationData({
            stationType: '1',
            stationName: path.destStationName,
            lineId: path.endLineId || '',
            stationNum: 0
        }));
    }

    return stations;
};

/**
 * 创建标准化的站点数据对象
 * @param {Object} data - 站点原始数据
 * @returns {Object} - 标准化的站点数据
 */
const createStationData = ({ stationType, stationName, lineId, stationNum }) => ({
    stationType,
    stationName,
    lineName: `${lineId}号线`,
    stationNum
});

// 查询按钮点击处理
const handleQuery = async () => {
    // ... 验证代码 ...
    try {
        const response = await api.findOdPath(params);

        if (response && Array.isArray(response) && response.length > 0) {
            // 使用processPathData处理每条路径数据
            pathResults.value = response.map(processPathData);
            // ... 选择路径代码 ...
        }
    } catch (error) {
        // ... 错误处理 ...
    }
};

// watch中的数据处理
watch(() => props.selectedPath, (newPath) => {
    // ... 其他代码 ...
    if (newPath) {
        try {
            // ... 线路和站点处理代码 ...
            // 使用processPathData处理路径数据
            pathResults.value = [processPathData(newPath)];
            selectedPathIndex.value = 0;
        } catch (error) {
            // ... 错误处理 ...
        }
    }
}, { immediate: true, deep: true });

代码结构对比

  • 修改前

    • 数据处理逻辑分散在多处
    • 相同的数据转换逻辑在不同地方重复
    • 缺乏统一的数据处理入口
    • 字段名称不统一(有些地方用allNum,有些地方没有)
  • 修改后

    • 引入了集中的数据处理函数processPathData
    • 添加了辅助函数createStationData标准化站点数据
    • 统一了字段命名(使用totalStations
    • 添加了详细的函数注释

维护性对比

  • 修改前

    • 修改字段名需要在多处搜索和替换
    • 添加新字段需要修改多个地方
    • 数据格式转换逻辑分散,难以统一修改
  • 修改后

    • 修改字段名只需在processPathData函数中更新
    • 添加新字段只需在processPathData和接口定义中添加
    • 数据格式转换集中在专门的函数中,便于统一修改

三、"一处修改"原则的实现与验证

为了验证"一处修改"原则的实现效果,可以通过几个常见的变更场景来对比:

场景1:修改字段名

需求 :将总站数的字段名从allNum改为stationTotal

修改前需要的操作

  1. 搜索所有使用allNum的地方(至少2处:handleQuery和模板)
  2. 修改每个地方的字段名
  3. 确保没有遗漏任何使用点

修改后只需的操作

  1. processPathData函数中修改:
javascript 复制代码
return {
    // ...其他字段
    totalStations: rawPath.stationTotal || 0 // 从allNum改为stationTotal
};
  1. 所有使用totalStations的地方自动使用新的数据来源

场景2:添加新字段

需求 :添加一个新字段estimatedArrivalTime(预计到达时间)

修改前需要的操作

  1. 在handleQuery中添加字段
  2. 在watch处理中添加字段
  3. 在接口定义中添加字段

修改后只需的操作

  1. processPathData函数中添加:
javascript 复制代码
return {
    // ...其他字段
    estimatedArrivalTime: rawPath.estimatedArrivalTime || null
};
  1. PathResult接口中添加:
typescript 复制代码
interface PathResult {
    // ...其他字段
    estimatedArrivalTime?: Date | null;
}

场景3:修改线路名称格式

需求:将线路名称格式从"X号线"改为"Line X"

修改前需要的操作

  1. 搜索所有创建lineName的地方(至少6处)
  2. 修改每个地方的格式转换逻辑

修改后只需的操作

  1. 只需修改createStationData函数:
javascript 复制代码
const createStationData = ({ stationType, stationName, lineId, stationNum }) => ({
    stationType,
    stationName,
    lineName: `Line ${lineId}`, // 从"X号线"改为"Line X"
    stationNum
});

四、优化收益分析

1. 功能正确性提升

  • 线路名称显示正确:用户现在能看到正确的线路名称,提高了信息准确性
  • 数据一致性 :统一使用接口返回的 allNum 值,确保与后端数据一致

2. 代码可维护性提升

  • 集中的数据处理:所有数据转换逻辑都集中在几个相关函数中
  • 单一修改点:修改字段名或添加新字段只需在一处修改
  • 清晰的职责划分:每个函数都有明确的职责和注释

3. 开发效率提升

  • 降低维护成本:后续需求变更时,只需修改相应的处理函数
  • 减少错误风险:标准化的数据处理减少了手动处理的错误可能性
  • 提高代码可读性:函数命名和结构更加清晰,新开发者更容易理解

4. 代码量对比

  • 修改前:数据处理逻辑约120行代码,分散在多处
  • 修改后:数据处理逻辑约100行代码,集中在专门的函数中

五、潜在风险与影响面

1. 风险点

  • 数据依赖性 :现在完全依赖接口返回的 allNum 值,如果接口不返回该字段,将显示为0
  • 渲染一致性 :如果其他组件直接使用 allNum 而非 totalStations,可能导致显示不一致

2. 影响范围

  • 直接影响station-display.vue 组件中的路径显示
  • 间接影响:任何依赖此组件输出数据的父组件
  • 用户影响:用户将看到更准确的线路名称和站点信息

六、总结

此次优化通过两个关键改动点解决了显示问题并提高了代码质量:

  1. 修正线路名称显示:确保连接线显示正确的线路信息,提高了用户体验
  2. 重构数据处理逻辑:通过引入专门的数据处理函数,实现了"一处修改"原则

通过前后代码对比,可以清晰地看到重构后的代码结构更加清晰、职责划分更加明确,维护成本显著降低。特别是在面对字段变更、数据格式调整等常见需求时,新的代码结构可以大大减少修改点,降低出错风险。

虽然createStationData等辅助函数看起来很简单,但它们是整个数据处理架构的重要组成部分,为代码的长期维护奠定了基础。这种渐进式的重构方法在不影响现有功能的前提下,有效提升了代码质量,为后续的功能迭代和维护创造了更好的条件。

相关推荐
Blue.ztl6 分钟前
菜鸟之路Day23一一JavaScript 入门
开发语言·javascript·ecmascript
巽星石16 分钟前
【Web】HTML5 Canvas 2D绘图的封装
前端·es6·html5·canvas·
ningmengjing_18 分钟前
《HTML + CSS + JS 打造炫酷轮播图详解》
javascript·css·html
小杰~28 分钟前
轻量级模块化前端框架:快速构建强大的Web界面
前端·前端框架
软件技术NINI33 分钟前
html css 网页制作成品——HTML+CSS非遗文化昆曲网页设计(4页)附源码
javascript·css·html
仰望丨苍穹34 分钟前
JavaScript性能优化实战
前端·javascript·性能优化
噔噔噔噔@34 分钟前
JavaScript性能优化的几个方面入手
开发语言·javascript·性能优化
JavinLu40 分钟前
idea超级AI插件,让 AI 为 Java 工程师
java·前端·intellij-idea
情绪羊1 小时前
Typescript Go 尝鲜体验指南
前端·typescript·github
ConardLi1 小时前
微调数据集太难搞?我直接手搓一个开源项目!
前端·javascript·人工智能