From 115f9b0acc1786260079de2facd3a114ac186eaf Mon Sep 17 00:00:00 2001 From: PaloMiku Date: Sat, 3 Oct 2026 21:10:34 +0800 Subject: [PATCH] =?UTF-8?q?fix(theme):=20=E4=BF=AE=E5=A4=8D=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=E5=AF=BC=E8=88=AA=E7=9A=84=E6=B7=B1=E8=89=B2?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E9=97=AA=E7=83=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 从首页切到文档时若已是夜间模式,会先闪一下日间再切回夜间。硬导航不受影响, 只有 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" 保留。探针与对照代码均已清除。 --- src/layouts/base.astro | 45 +++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/src/layouts/base.astro b/src/layouts/base.astro index 8f0da6e..f6345e0 100644 --- a/src/layouts/base.astro +++ b/src/layouts/base.astro @@ -73,16 +73,47 @@ const ogImage = new URL(image ?? '/og.png', Astro.site).href;