上一篇说过,列表页的“忙”不是一个,是几个。这一篇把这些“忙”落成三层,每一层有自己的归属和关门条件。分层以后,交给 Codex 的任务就从“加个 loading”变成“给这个页面找出几个等待,每个等待归谁管”。

分层不是命名游戏。真正的依据是生命周期:谁触发的、等谁的、谁有权结束。

页面级:遮罩只认首屏,不认每一次请求

页面级的等待起点是页面挂载,终点是首屏数据就绪。它的作用是把整页空白挡在后面,遮罩一关,页面就进入可用状态。

这一层最容易写错的地方,是让它跟着后续每一次翻页、查询开开关关。首屏过后再查一遍数据,页面本身不需要重新遮罩,遮罩反复出现反而打断操作。

结构示意:

const firstLoad = ref(true);
const list = ref([]);
​
const initList = async () => {
  firstLoad.value = true;
  try {
    list.value = await listApi({ page: 1, size: 10 });
  } finally {
    firstLoad.value = false;
  }
};

finally 只是确保这个状态一定会复位,不代表它解决了并发。首屏和翻页如果共用同一个请求入口,还需要请求资格判断,这一点上一篇已经拆过。

按钮级:节流和去重是两件事

按钮级的等待属于一次提交,起点是点击,终点是这次提交的响应。它要解决的是用户手快连点两次。

const saving = ref(false);
​
const onSave = async (form) => {
  if (saving.value) return;
  saving.value = true;
  try {
    await saveApi(form);
  } finally {
    saving.value = false;
  }
};

这里挡住的只是“同一时间只发一次”。还有一类问题它挡不住:请求已经发出、响应也回来了,用户刷新页面或后退后再点一次,同一份表单又提交了一份。那是幂等和去重的事,不是节流的事。

web-skills 把按钮节流交给 btnState 配置,在请求拦截器里打开、响应或异常里关闭。它管的是节流这一段,不负责业务幂等。给 Codex 描述按钮状态时,要先分清这次要解决的是连点,还是重复数据,两个目标对应两套机制。

局部级:表格区域和弹框各自关门

局部级的等待发生在页面已经可用之后,只影响一小块区域。典型的是表格查询和弹框内加载。

const listLoading = ref(false);
const dialogLoading = ref(false);
​
const loadList = async (params) => {
  listLoading.value = true;
  try {
    return await listApi(params);
  } finally {
    listLoading.value = false;
  }
};

表格的 listLoading 只跟随列表请求,弹框的 dialogLoading 只跟随弹框请求。它们不该互相覆盖,也不该去动页面级的遮罩。

web-skillsloadingdialogLoading 两个独立配置项把这两段分开。目标项目若没有这套封装,至少要保证:一个区域的状态,不会因为另一个区域的请求结束而被关掉。

局部级还有一个并发细节。两次翻页快速切换时,第一次请求后返回也会执行 finally,把 listLoading 提前关掉。所以局部状态同样要配请求资格判断,否则分层了,遮罩还是会被旧响应提前收走。

三层之间的关闭条件,先写清楚再让 Codex 动手

层级 触发者 关闭条件 常见错写
页面级 页面挂载 首屏数据 settle 跟随每次查询反复遮罩
按钮级 用户点击 本次提交 settle 用全局 loading 代替按钮状态
局部级 区域请求 该区域最新请求 settle 被其它区域或旧响应关闭

这张表交给 Codex 之前,先确认“settle”在项目里的含义。有的团队只要请求返回就关,有的要求数据已写入状态再关。口径不统一,分层再清楚也会在交接处出缝。

分层后的验收,一半靠读代码,一半靠跑页面

加载状态没法只靠静态审查交付,有一部分必须打开页面才能确认。

静态能查的:每个 loading 状态有没有独立的来源;关闭发生在响应成功、失败还是 finally;有没有哪个状态被多个请求共用。

要跑页面的:并发翻页时遮罩会不会被旧响应提前关掉;保存期间表格查询的 loading 是否被误关;首屏遮罩关掉后表格是否已就绪。这些需要一次真实操作,或者明确记为未验证,不能靠读代码下结论。

交给 Codex 的任务模板

请为这张列表页整理加载状态,不先改代码:
​
1. 列出页面里所有“忙”的场景:首屏、翻页、查询、刷新、保存、弹框加载等。
2. 为每个场景标出层级:页面级、按钮级还是局部级。
3. 记录每个状态当前由谁写入、在哪个请求或生命周期里关闭。
4. 找出被两个以上请求共用的状态,说明谁先返回会影响谁。
5. 区分按钮节流和业务幂等,指出本项目当前只解决了哪个。
​
先给出分层表和关闭条件,再给最小修改方案。不要新增一个统一 loading 把现有状态包起来,也不要绕开项目已有的请求封装。

这条任务的第一句就是“不先改代码”。加载状态的混乱,大多是因为动手之前没人把“几个忙、各归谁”数清楚。数清楚了,改哪里通常是显而易见的。

写在最后

分层解决的不是命名问题,是关门权。页面遮罩、按钮状态、区域加载各有各的生命周期,把它们塞进一个布尔值,遮罩就会在错误的时间消失。

按页面、按钮、局部三层划清归属和关闭条件,再配合请求资格判断,加载状态才从“加了个变量”变成“每条等待都有出处”。

下一篇进到按钮这一层,专门讲重复提交:连点、双击、提交后刷新、接口重试,分别该用节流、去重还是幂等来挡,而不是一律塞进 btnState

本系列持续更新。列表页的加载和提交状态收束后,会回到页面异常路径,看接口失败时列表应该如何退化。

参考资料

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐