票据索引技术封面

「票据索引」初版最值得研究的不是页面长什么样,而是一个具体故障:筛选后卡片闪动且选中态错位。我把它当作列表一致性问题,从输入、处理和输出三个方向核对,最后发现关键不在多写几个条件判断,而在于确定数据归属。

一、问题与工程边界

在「票据索引」里,用户首先遇到的是“筛选后卡片闪动且选中态错位”。这不是换一个按钮颜色就能解决的视觉缺陷。稳定标识与差量刷新如果缺少稳定约束,页面在短时间内可能看起来正常,一旦操作顺序、数据规模或窗口条件发生变化,结果就会偏离预期。例如在复现时先保留一组确定输入,然后只改变列表一致性相关的一项条件,观察输出是否严格遵循“让数据来源和组件生命周期解耦”的原则。

工程上先承认一个前提:业务 ID 不随位置变化,渲染标识也不能使用索引。因此设计不能只盯着LazyForEach这个接口名称,而要把调用方、状态拥有者、更新时机和失败后的回退放在同一张关系图里。这也说明,真正需要固定的是判断规则,不是视觉呈现形式;稳定标识与差量刷新的变化必须能够解释,才能成为可以长期维护的方案。

把筛选数据、稳定键映射、局部通知、保持选择拆成几个可观测节点后,问题会清晰得多。输入数据在第一个节点取得,后续每一步只变换自己负责的字段;最终结果由指定所有者提交,不允许多个组件同时改写同一个含义。若多个步骤共用同一数据,建议为数据附加版本或稳定标识,以便区分本次操作与过期结果,避免无意覆盖。

二、数据模型和关键约束

这里最容易犯的错误是数组下标作为永久 key。这种写法短期内省掉了转换或校验,但同时把边界隐含在调用顺序中。界面或任务稍有变化,异常就会以完全不同的形式出现。一种简单的评审办法是让同事分别从正常路径和异常路径解释代码。只要两种解释出现冲突,说明接口契约仍然不够清楚。

票据索引开发或运行界面

本例采用的主线是“让数据来源和组件生命周期解耦”。与其在每个使用点各补一段判断,不如把核心判断封装成单独函数,再让界面只展示确定的结果;这样功能测试也不需要依赖页面能否完整启动。把验证步骤记录下来之后,重构便有了可靠边界:改动可以优化结构,但不能改变对用户已经承诺的行为。

对列表一致性而言,原始数据、派生状态和呈现效果是三层不同的概念。原始数据用于追溯,派生状态用于业务判定,呈现效果只负责告知用户。把这三层捆在一起,会让后续修改的影响范围难以预测。这些判断不需要依赖虚构的性能数据;从输入、转换、输出和清理职责是否明确,就能评估设计是否稳健。

三、代码实现与调用路径

定位筛选后卡片闪动且选中态错位不应靠随机注释代码。先固定复现输入,再逐层检查筛选数据、稳定键映射、局部通知、保持选择中每个节点的前后差值;只有出现第一次不合理结果的位置,才值得投入时间修复。例如在复现时先保留一组确定输入,然后只改变列表一致性相关的一项条件,观察输出是否严格遵循“让数据来源和组件生命周期解耦”的原则。

TicketList.ets:核心处理片段

下面的代码展示了与「列表一致性」直接相关的最小处理单元。它强调数据边界和调用顺序;没有包含的工程配置应根据所用 SDK 模板补全。

interface Ticket { id: string; name: string }
class TicketData {
  private source: Ticket[] = []
  replace(next: Ticket[]): void { this.source = next.map(v => ({ ...v })) }
  totalCount(): number { return this.source.length }
  get(index: number): Ticket { return this.source[index] }
  key(index: number): string { return this.source[index].id }
}
// 实际 LazyForEach 应配合 IDataSource 的变更通知接入。

不是所有优化都值得马上做。对于票据索引这种规模,首先保证业务 ID 不随位置变化,渲染标识也不能使用索引,再讨论是否需要缓存、延迟更新或减少对象创建。否则所谓优化可能把一处可见的错误变成多个不易复现的隐患。这也说明,真正需要固定的是判断规则,不是视觉呈现形式;稳定标识与差量刷新的变化必须能够解释,才能成为可以长期维护的方案。

测试不能停留在“看上去没问题”。建议把筛选后卡片闪动且选中态错位编成固定回归用例,并增加正常输入、空值、重复触发、快速切换和窗口变化五类条件。每一类都记录可预期的具体状态,而不是笼统判断成功。若多个步骤共用同一数据,建议为数据附加版本或稳定标识,以便区分本次操作与过期结果,避免无意覆盖。

失败处理的目标不是吞掉异常,而是维持稳定标识与差量刷新的内部一致性。某一步没完成时,保留已确认的稳定状态;临时对象应明确释放,界面展示可重试的结果,避免下一次操作继续读到半成品。一种简单的评审办法是让同事分别从正常路径和异常路径解释代码。只要两种解释出现冲突,说明接口契约仍然不够清楚。

票据索引关键机制与状态

四、异常分支及故障分析

后续如果票据索引加入更多功能,不要在现有核心函数里无限添加分支。先分析新增能力改变的是输入、规则还是输出;只调整对应层次,并保持既有列表一致性契约不变。把验证步骤记录下来之后,重构便有了可靠边界:改动可以优化结构,但不能改变对用户已经承诺的行为。

多人协作时,最重要的不是大家都懂LazyForEach,而是统一“何时允许修改、何时必须通知、何时执行清理”。这些规则写在接口注释和测试用例里,比口头约定更不容易在重构时丢失。这些判断不需要依赖虚构的性能数据;从输入、转换、输出和清理职责是否明确,就能评估设计是否稳健。

安全与正确性并非两回事。筛选数据、稳定键映射、局部通知、保持选择的每个入口都应检查数据是否来自可信源,是否超过可接受长度,是否包含不合法状态。尤其不能把示例页面中的固定参数,当成真实业务中的安全证明。例如在复现时先保留一组确定输入,然后只改变列表一致性相关的一项条件,观察输出是否严格遵循“让数据来源和组件生命周期解耦”的原则。

五、验证方法与适用条件

窗口尺寸、屏幕密度、字体放大和后台恢复都可能改变票据索引的呈现条件。适配时首先保证逻辑结果不受设备变化影响,再单独计算布局与输入映射;不要依赖某一台设备的固定像素值。这也说明,真正需要固定的是判断规则,不是视觉呈现形式;稳定标识与差量刷新的变化必须能够解释,才能成为可以长期维护的方案。

调试记录应围绕可比较的信息组织:操作名称、业务标识、输入版本、决定采用的分支、处理前后状态。对于列表一致性,这些字段通常比堆砌整份原始对象更有定位价值,也更容易避免敏感内容外泄。若多个步骤共用同一数据,建议为数据附加版本或稳定标识,以便区分本次操作与过期结果,避免无意覆盖。
将票据索引的输入统一成一个能够检查的结构,是这次调整的起点。以列表一致性为例,至少记录操作编号、当前数据版本、输入来源以及进入组件时的坐标或参数。每次产生新结果时先核对编号,再检查结果是否对应最新版本。旧操作即使晚一步完成,也只能成为可丢弃的历史信息,不能改写当下已确认的界面。这比收到结果以后再猜测用户做了什么更可靠。

实现时建议在TicketList.ets旁边保留一段小型纯函数,专门实现“让数据来源和组件生命周期解耦”这项规则。纯函数不读取页面私有字段,不改变窗口对象,也不负责弹出提示;它接收明确的值并返回明确的结果。这样可以对边界输入进行表格化测试,也便于在组件升级后检查规则是否改变。即使未来LazyForEach的调用形式调整,最容易出错的决策仍然留在可验证的位置。

一条有效的正常路径可以分为四个观察点:开始时列表一致性处于初始值,输入进入后得到合法的中间表示,操作完成后界面只提交一次结果,退出时临时资源回到空闲状态。测试者要逐个截取这四处信息,而不是只核对最后一张页面。对于“筛选后卡片闪动且选中态错位”,还应故意改变操作顺序,观察是否会得到不一致的结果。

交付前重新走一遍筛选数据、稳定键映射、局部通知、保持选择,核对空态、错误态、重复操作、资源释放以及页面退出的行为。票据索引的验收标准应以业务 ID 不随位置变化,渲染标识也不能使用索引是否被保持为准,而不是单张效果图有多精致。一种简单的评审办法是让同事分别从正常路径和异常路径解释代码。只要两种解释出现冲突,说明接口契约仍然不够清楚。

参考资料

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐