有一次前端审计,最初看起来像一项很普通的检查:打开页面,看看布局有没有错,点几下按钮,再截几张图。页面确实能加载出来,主视图也有内容,第一眼没有明显的“打不开”问题。
但真正开始沿着用户路径走,我发现这件事远没有这么简单。真实矩阵接口返回了 404,部分选题缺少可发等级和发布账号字段;轮询之后,账号按钮会丢失焦点;展开选题后,焦点甚至会回到 BODY;矩阵页签进入后,也没有办法顺利返回选题。最后还需要在 1440、1024、375 3 个尺寸下分别确认布局,而不是用桌面宽度的截图替代全部结论。
这次经历让我重新理解了“验收通过”这句话。页面能打开,只能证明入口没有立刻崩溃,不能证明用户能完成任务。
页面打开只是验收的起点
当时我先把审计范围摊开,里面不只有视觉细节,还包括键盘路径、焦点、加载状态、错误状态、空状态、轮询竞态、链接可达性、响应式和性能。这样做不是为了把检查表做得很长,而是因为一个真实用户不会只看首页的第一屏。
用户可能要先找到一条选题,再展开它的拆解信息,然后切换到矩阵查看结果,最后还要回到原来的视图继续操作。每一步都有状态变化,也都有可能把用户从原来的路径上甩出去。只要其中一个环节失效,“页面能打开”就没有太大意义。
前端验收的对象不是一个能显示出来的页面,而是一条用户可以走完的任务路径。
如果不把路径写出来,验收很容易停在最有利于页面的一刻:初始数据已经出现,按钮看起来像按钮,卡片也没有溢出屏幕。可是用户真正要做的事情通常发生在点击之后、等待之后和切换之后。问题也往往藏在那里。
所以我没有先从颜色、间距这些视觉细节开始,而是先问:用户从进入页面到完成任务,需要经过哪几步?每一步会触发什么请求?内容为空、请求失败或数据晚到时,用户还能不能知道下一步做什么?
真实接口会拆穿模拟成功
这次最直接的发现来自真实数据。矩阵接口实测返回 404,而不是一个结构完整的成功响应。与此同时,5 个账号的选题数据里,还缺少可发等级和发布账号字段。这样的结果不能被“页面主体已经渲染”掩盖,因为它说明前端看到的成功态并不等于后端提供了完成任务所需的数据。
这里最容易出现的错误捷径,是用一份内存里的模拟数据把矩阵页面跑通,然后把截图当作真实验收结果。模拟数据当然有价值,它可以帮助我们检查成功态布局、表格层级和不同尺寸下的排版。但它不能证明真实接口可达,也不能证明字段在实际响应中存在。
我选择把两种结果分开记录,是因为它们回答的是不同问题:模拟成功态回答“前端在数据齐全时能不能呈现”;真实请求回答“当前系统是否真的能把用户带到这个状态”。把两者混成一个“通过”,后续排查的人就不知道问题在服务端、数据契约还是页面逻辑里。
真实请求失败时,前端成功态的截图只能证明展示能力,不能替代接口验收。
这也解释了为什么错误状态和空状态不能最后再补。接口返回 404 时,用户需要看到可理解的失败反馈;字段缺失时,页面不能把空白当成“没有内容”而静默吞掉。否则用户会以为自己没有权限、没有选题,或者误以为按钮本来就不能用。
一个比较稳妥的记录方法,是给每条结论标注来源:这是浏览器实测、静态推导,还是被服务端问题阻塞。这样做看起来只是写字更仔细,实际上是在保护判断边界。没有亲自验证的内容,就不应该写成已经发生的事实。
焦点问题比视觉问题更隐蔽
页面截图很难告诉我焦点是否还在正确位置。于是我沿着键盘路径和点击后的状态变化重新走了一遍。结果发现,轮询发生后,账号按钮的焦点会丢失;展开选题之后,焦点会回到 BODY。鼠标用户可能只觉得页面“没有反应”,键盘用户则会直接失去当前位置,不知道下一步该按什么。
这类问题之所以容易漏掉,是因为页面仍然在显示,数据也可能仍然在刷新。视觉上没有红色报错,开发者工具里也不一定有异常,只有当人连续操作时,才会发现操作上下文断掉了。
表面上我是在检查一个焦点属性,其实是在检查页面有没有记住用户正在做什么。一个人打开选题,是带着明确意图来的;如果内容更新后焦点跳走,他就必须重新寻找位置。对于依赖键盘的人,这不是体验小瑕疵,而是任务路径被打断。
如果不处理,后面会出现的问题不只是“键盘不够顺手”:屏幕阅读器用户可能无法得知内容是否展开,连续刷新还可能让用户误触其他控件,焦点跳到 BODY 后,页面也失去了可预测性。一个看似偶发的刷新问题,最后会变成用户不敢操作的页面。
我没有把“能用鼠标点通”当作焦点问题已经解决,是因为鼠标路径和键盘路径依赖的反馈不同。验收时需要在关键动作后确认焦点落点:展开之后仍在合理位置,异步刷新之后不会被重建控件带走,切换视图之后还能继续找到返回入口。
响应式验收要看任务是否仍然成立
这次检查了 1440、1024 和 375 3 个尺寸。不同尺寸的意义,不是分别截一张图证明页面会缩放,而是看同一条任务路径在不同空间里是否仍然成立。
宽屏下,信息可以并排出现,问题往往藏在层级关系里;中等宽度下,按钮、卡片和页签开始争夺空间;到了 375 宽度,用户可能需要滚动、展开或切换。只看 1440 的截图,很容易把“看起来完整”误当成“用户可以操作”。
我更关注几个具体问题:关键按钮是否仍然可见,展开内容是否把后续操作推到不可发现的位置,页签是否还能返回,链接是否真正可达,以及错误或空状态出现时是否有足够的空间说明原因。响应式不是把大盒子压窄,而是要在空间变化后保留任务的顺序和可达性。
矩阵页签无法返回选题,就是一个很典型的例子。它不一定在静态截图中显得严重,但用户一旦进入矩阵,就失去了原本的导航回路。这个问题提醒我,响应式、路由和焦点不能完全分开验收,它们共同决定用户还能不能回到下一步。
不同视口的验收标准不是画面相似,而是同一任务仍能被发现、执行和返回。
把验收结论分成可复核的几类
一轮审计如果只留下“有问题”和“没问题”,很快就会陷入争论。有人说接口暂时不可用,有人说前端模拟态已经通过;有人认为焦点只是细节,有人认为它影响无障碍。更好的做法,是让每条结论都带着证据类型和影响范围。
我会把检查结果拆成 3 类。第一类是实测:例如真实矩阵接口返回 404,或者轮询后焦点丢失。第二类是静态推导:代码结构显示某个控件在刷新时会被重建,但尚未在真实环境中触发。第三类是服务端阻塞:前端暂时无法获得完整数据,因此只能用模拟成功态检查布局,不能宣布真实链路通过。
这样的分类不是给报告增加格式,而是避免把不同性质的问题排在同一条线上。接口 404 需要服务端或数据契约继续处理;焦点丢失需要前端交互修正;模拟数据验证成功态,则应该保留为有限范围的证据。每件事都有下一步,但下一步不一样。
可复用的方法是,先写用户任务路径,再为每个节点准备 4 组检查:真实请求、键盘与焦点、不同视口、加载和失败状态。最后把结论写成“我看到了什么、在什么条件下看到、它影响哪一步、还有什么没有验证”。这样下一位接手的人无需重新猜测,也不会把局部成功扩大成整体通过。
下次我会怎么做
下次遇到类似的前端验收,我会先暂停截图,先画出最短的用户路径:进入、读取、展开、切换、返回、完成。然后逐步验证每一步的真实请求和数据字段,再补键盘、焦点、错误、空状态与 3 个代表性视口。
我会把成功态和失败态分开,把真实接口和模拟数据分开,把已经实测和静态推导分开。如果某个服务端问题阻塞了完整验收,我会明确写出阻塞点,同时继续完成不依赖它的布局和交互检查。这样既不会把外部故障误算成前端缺陷,也不会借助模拟成功把真实问题藏起来。
这件事让我重新意识到,验收不是给页面盖一个“能打开”的章,而是替用户提前走一遍可能中断的路。一个流程成熟的标志,不是它在成功时看起来多完整,而是它在失败、等待和切换之后仍然让人知道自己在哪里、下一步能做什么。