一次会误导验证结果的故障

Yggdrasil 在 2026 年 10 月的 Leptos 懒路由排障中发现:导航到慢分包页面,加载中后退,首页看起来已经恢复;但再点另一个页面时,地址变化,正文却不再更新。

“回到首页、透明度恢复、pending 清空”只能证明外观恢复,不能证明 WASM 和路由状态仍然存活。

以下内容整理自当时的历史实验,未在本文整理时重新复现,也不据此判断当前上游版本是否仍有问题。

最小验证场景要多走一步

当时的故障注入方式是延迟 /pkg/ 下的 WASM 请求,再在导航挂起中触发后退。

有效的验证步骤是:

  1. 从一个已完成水合的页面出发。
  2. 延迟目标懒路由的 WASM 分包请求。
  3. 导航到该页面,在 pending 期间后退。
  4. 确认原页面恢复。
  5. 再导航到第三个页面,同时核对 URL、正文和控制台。
  6. 补测不后退、直接再次 push 到第三页的情况。

最后两步能区分“视觉状态清理成功”和“路由运行时仍然可用”。

根因是数据 owner 比使用它的视图先销毁

当时定位到 leptos_router 0.8.17 的导航取消路径:旧页面的数据 owner 被移入可中止的预加载 future;新导航中止该 future 后,旧 owner 随之销毁,但旧视图仍显示并读取其中的 Memo,最终 panic。

原型修复让数据 owner 的存活时间不短于使用它的视图,并用“慢分包后退”和“慢分包中再次导航”两条路径验证。这个结论来自特定源码版本,不能简单推广为所有路由器都有同样问题。

动画播放中再次导航,避免重新截图

另一类问题发生在原生 View Transition 已 ready、正在播放动画时:先 skipTransition,再启动新过渡,会让刚进入的页面瞬间恢复为全亮,被截成下一次旧快照,然后再次淡出,视觉上像闪烁。

项目改为接力:沿用正在播放的原生过渡,直接提交新的导航。新快照是实时画面,因此无需为每次连续导航重新建一套过渡;同时避免旧页面重复播放进入动画。

这不是所有阶段都可无条件复用:截图后、ready 前仍需处理待提交导航;项目在该阶段排队,ready 后再接力。

点击失效也可能来自过渡命中测试

历史 Chromium 实测中,部分过渡阶段的点击命中落在 HTML,elementFromPoint 也返回 HTML,页头链接因此被吞。

项目针对已知的页头导航区域,按点击坐标找回实际链接。复现时要检查事件命中元素和过渡阶段,不能仅凭“点了没反应”认定链接监听器没有绑定。

防止交叉淡化中间变暗

旧页和新页使用不同的透明度曲线时,两者合计不透明度可能在中间大幅下降。项目让新旧透明度使用同一条互补曲线,位移仍使用独立的弹簧曲线。

验证时可以录屏或逐帧比较正文亮度,同时记录 startViewTransition 的阶段时间。只看动画结束截图会漏掉中间的暗帧。

来源

Yggdrasil 2026-10-27 至 2026-10-28 导航排障记录;视图过渡修复 edc36cdc;上游数据 owner 问题关联 Leptos PR #4973。PR 的当前状态应另行查询。