fix(theme): 修复客户端导航的深色模式闪烁 - #49
Merged
Merged
Conversation
从首页切到文档时若已是夜间模式,会先闪一下日间再切回夜间。硬导航不受影响, 只有 ClientRouter 客户端导航会闪。 根因:Astro 的 swapRootAttributes()(astro/dist/transitions/swap-functions.js:46-54) 用 filter 的副作用剥掉 html 元素上除 NON_OVERRIDABLE_ASTRO_ATTRS(仅 data-astro-transition 与 -fallback)外的全部属性,而服务端产物本身不带 class,于是 dark 丢失。router.js 里 doSwap() 完成属性交换(第 202 行)到 runScripts() 重跑内联防闪脚本(第 351 行)之间跨了 一个 await,且导航走 startViewTransition,该中间态会被快照交叉淡入,于是肉眼可见。 修复:注册 astro:before-swap 监听器,在交换发生前把当前主题写进 event.newDocument。 该事件由 doSwap 先 dispatchEvent 再 event.swap() 派发,携带 newDocument (events.js:117-118),因此交换过程本身不再产生中间态。以 html 元素的实时 class 为真值 而非 localStorage,用户中途切过主题或 system 跟随系统变化时都能正确带上。 顺带把 localStorage 读取拆出 try/catch(原先整个解析逻辑都在 try 内,matchMedia 抛错会 连主题判断一起吞掉),主题判断抽成 resolveDark() 供硬导航与后续共用。 验证:临时插入 MutationObserver 探针监视 html 元素的 class 变化,并做反向对照(故意 摘掉修复跑一次)以确认探针敏感。结果:无修复时 darkLost=1、finalDark=false,丢失发生在 navDepth=2(before-preparation 与 before-swap 之后);装回修复后 darkLost=0、 finalDark=true。导航后 DOM 实测 class="dark" 保留。探针与对照代码均已清除。
SafeDep Report SummaryNo dependency changes detected. Nothing to scan. This report is generated by SafeDep GitHub App |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



现象
从首页切到文档时,若用户已是夜间模式,会先闪一下日间再切回夜间。硬导航(F5 / 直接打开 URL)不受影响,只有 ClientRouter 客户端导航会闪。
根因
swapRootAttributes()(astro/dist/transitions/swap-functions.js:46-54):filter的副作用把<html>上的属性挨个removeAttribute掉,只保留NON_OVERRIDABLE_ASTRO_ATTRS—— 而它只有data-astro-transition与data-astro-transition-fallback两项,class不在内。再叠加服务端产物<html lang="zh-CN">本身不带 class,dark就此丢失。时序上,
router.js里doSwap()完成属性交换(第 202 行)到runScripts()重跑内联防闪脚本(第 351 行)之间跨了一个await;且导航走startViewTransition,该中间态会被快照并交叉淡入,因此肉眼可见。修复
在原有防闪脚本里注册
astro:before-swap监听器,在交换发生前把当前主题写进event.newDocument:该事件由
doSwap先dispatchEvent再event.swap()派发,携带newDocument(events.js:117-118),因此交换过程本身不再产生中间态。两个设计点:
<html>的实时 class 为真值,而不是回读localStorage。用户中途切过主题、或system跟随系统变化时,实时 class 才是当前真相。__mxThemeSync全局标志保证只注册一次。这个is:inline脚本每次导航都会被runScripts()重跑,不加守卫会重复注册。顺带把
localStorage读取拆出try/catch(原先整个解析逻辑都在 try 内,matchMedia抛错会连主题判断一起吞掉),主题判断抽成resolveDark()供硬导航与后续共用。验证
单看"不闪了"说明不了什么 —— 可能是探针根本没在跑。所以临时插入了一个
MutationObserver探针监视<html>的 class 变化,并做了反向对照(故意摘掉修复跑一次)以确认探针敏感:darkLostfinalDarknavDepth=2navDepth=2正是「before-preparation与before-swap均已触发、swap 正在执行」的时刻,与从源码推的时序吻合。导航后 DOM 实测:探针与对照代码均已清除(已校验 7 个标记字符串在源码与产物中均不存在)。
回归
pnpm build55 页零警告 ·pnpm check0 error / 1 hint ·pnpm lint通过 ·pnpm pangu通过 ·check-links0 阻塞。已知未覆盖
浏览器实测只跑了「首页 → 文档」这一条(即原始报告路径)。修复挂在共用的
base.astro上,对所有客户端导航生效,__mxThemeSync守卫也经得起推敲(window/document在视图切换中不被替换);但文档内互跳与返回首页未逐一跑完 —— 那几次点击因运行时对 SPA 导航的检测不稳定而超时,非修复本身问题。