Skip to content

🐛 批量更新页打开更新详情复用检查缓存并挡住重复点击 - #1719

Open
CodFrm wants to merge 1 commit into
mainfrom
fix/batchupdate-open-detail
Open

🐛 批量更新页打开更新详情复用检查缓存并挡住重复点击#1719
CodFrm wants to merge 1 commit into
mainfrom
fix/batchupdate-open-detail

Conversation

@CodFrm

@CodFrm CodFrm commented Sep 3, 2026

Copy link
Copy Markdown
Member

Checklist / 检查清单

  • Fixes mentioned issues / 修复已提及的问题
  • Code reviewed by human / 代码通过人工检查
  • Changes tested / 已完成测试

N/A — 无关联 issue,问题由维护者在批量更新页直接反馈;Code reviewed by human 待人工审阅。

背景

src/batchupdate.html 上点击脚本名查看更新详情时:

  1. 要等好一会儿才打开更新详情页;
  2. 等待期间没有任何反馈,可以重复点击。

读代码定位到根因:检查更新阶段已经把新版代码拉下来存进了 ScriptUpdateCheck 的记录缓存(cacheFull.list[].newCode,行内「更新」按钮装的就是这份),但 openUpdatePageByUUIDopenUpdatePageopenUpdateOrInstallPage 第一句仍是 await fetchScriptBody(url),为每次点击白跑一次网络往返。而 openInCurrentTab 实际是 chrome.tabs.create,所以点几次就开几个安装页。

改前构建上的复现(本地 mock server 记录真实请求,同一 tick 连点 3 次):

hits pre-click: 3  →  hits post-click: 6        # 3 次点击 = 3 次重复下载
pages: install.html?uuid=37f9b7ee… ×3           # 开出 3 个安装页

本次改动

Service Worker(src/app/service/service_worker/script.ts

  • openUpdateOrInstallPage 拆成「拉代码」+「处理代码」两段,新增 prepareUpdateOrInstallPage;原方法退化为 fetchScriptBody 后交给它,静默更新判定、SkillScript 分支、临时代码写入逻辑原样保留。
  • openUpdatePage 增加可选 cachedNewCode:命中时直接进处理段,跳过网络拉取;并改为回报 boolean(打开安装页或完成静默更新为 true,拉取/解析失败为 false)。
  • openUpdatePageByUUID 从检查记录缓存取 newCode 传入,缓存缺失时按原路回退网络拉取;同时用 DAO 取到的完整 Script 直接传参,去掉原先拼半个对象所需的 @ts-ignore

页面(src/pages/batchupdate/

  • hooks.ts:新增 opening 状态与同步镜像 openingRefonOpen 在请求进行中拒绝同一行的重复点击,失败(返回 false 或消息通道 reject)弹 notify.error;点击脚本名同样取消自动关闭倒计时。
  • components.tsx / mobile.tsxScriptName 增加 loading,打开期间 disabled + aria-busy + 转圈,桌面与移动两套视图一致。
  • 新增文案 key install:updatepage.open_failed,10 个语言包已补齐。

实现考虑

  • 为什么改 SW 而不是只加个 loading:加遮罩只能让「白等一次网络」变得好看一点;这次点击本来就不需要联网。复用缓存代码还有一个正确性上的好处 —— 打开的详情页与列表上展示的、以及「更新」按钮会装的是同一版代码。
  • 静默更新语义不变:缓存路径与网络路径都走同一个 prepareUpdateOrInstallPage,静默更新开关命中时仍然返回 2、不开安装页。
  • 防连点用 ref 而不是 statesetState 是异步的,挡不住同一 tick 内的连击;openingRef 同步落地,opening state 只负责驱动转圈。
  • 缓存陈旧:检查记录可能不是最新一次网络内容,但这正是列表当前展示的那一版;batchUpdateListAction 的更新路径本来也用这份缓存,两者保持一致。

已知限制

  • 缓存被 Service Worker 回收后的「回退网络拉取 → 失败 → 弹提示」分支,只有单元测试覆盖:真实会话里无法制造 SW 被回收的状态。
  • 打开完成后再次点击仍会开出第二个安装页 —— 这是完成态的正常行为,本次只挡进行中的重复点击。

建议审查重点

  • prepareUpdateOrInstallPage 拆分后,安装(update=false)与更新(update=true)两条既有路径行为是否完全不变。
  • openUpdatePagevoid 改为 boolean 后,另一处调用方 requestCheckUpdate 不受影响。
  • 页面卸载(自动关闭 / 用户关页)时挂起的打开请求不会造成异常。

验证

npx vitest run src/pages/batchupdate src/app/service/service_worker/script.test.ts
# Test Files 4 passed | Tests 165 passed

pnpm run typecheck        # 通过
npx eslint <改动文件>       # 通过(pre-commit 钩子同样跑过 lint / prettier / i18n)
pnpm run check:i18n       # ✅ all locales match the en-US / en reference key sets

新增测试(均先写后红再实现):

  • script.test.ts —— 命中缓存不再拉取 / 缓存失效回退拉取 / 拉取失败回报 false / 脚本不存在回报 false
  • hooks.test.ts —— 打开期间标记进行中且重复点击不重复发起 / 失败弹提示并解除转圈 / 消息 reject 同样反馈 / 点击取消自动关闭
  • components.test.tsx —— 桌面行打开期间 aria-busy + 转圈且拒绝点击 / 非打开态可点击 / 移动卡片一致

真实浏览器验证(node e2e/session.mjs + 本地 mock server,非提交进仓库的用例):

改后 · 单击:      hits 2 → 2,新开 1 个 install.html,详情页显示 v1.0.0 → v2.0.0 与代码 diff
改后 · 连点 3 次: hits 2 → 2,只新开 1 个 install.html

已知的既有问题(与本 PR 无关,在干净树上同样失败):src/pages/install/useInstallData.test.ts 有 1 条用例失败。

Screenshots / 截图

改动为行内转圈与失败 toast 的交互反馈,主路径修复后打开近乎瞬时,静态截图无法体现差异;上文以 mock server 请求数与标签页数量作为证据。

点击脚本名查看更新时,openUpdatePageByUUID 会重新 fetch 一次脚本代码,
而这份新版代码在检查更新阶段已经存进 scriptUpdateCheck 的记录缓存里
(行内「更新」按钮装的就是它)。用户因此要为每次点击白等一次网络往返,
期间页面又没有任何反馈,连点几下就会开出多个安装页。

- SW: 拆出 prepareUpdateOrInstallPage,openUpdatePage 命中缓存代码时
  跳过 fetchScriptBody;openUpdatePageByUUID 改为回报 boolean
- 页面: 打开期间行内转圈并同步挡住重复点击,失败弹 toast,
  点击脚本名同样取消自动关闭倒计时
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant