博客导航栏抖动修复记
Technical
2026-05-10
612 字
3 分钟

这个 bug 折磨了我好几天。每次点博客导航栏上的链接,其他几个文字就像被电了一下,集体向右哆嗦一下再弹回来。不像什么大问题,但每次看到都很难受。

我尝试了不同的模型去修复这个问题,都没有找到真正的元凶

第一回合:怀疑字体#

我用的霞鹜文楷是 web font,font-display: swap 意味着页面先用系统字体渲染,等霞鹜文楷下载完了再替换。两种字体对同一个字母的宽度不一样——“Home”在 Roboto 下可能 42px 宽,霞鹜文楷下变成 47px。这 5px 的差距在 flex 布局里足以把旁边的元素挤开。

于是我把导航栏字体全改成了系统字体。心想这下总不会有字体替换了吧?部署上去一看——还在抖。

字体不是根因。

第二回合:怀疑 CSS 动画#

又检查了全局 CSS,发现几乎所有文字元素都挂了 transition。是不是点链接时 focus 状态触发了什么动画?把 outline 禁了,把颜色从半透明改成不透明,把 scrollbar-gutter 设成 stable……一套组合拳打下去,毫无改善。

CSS 动画也不是根因。

第三回合:偶然发现#

反复看 Navbar.astro 的代码,突然注意到一行:

<Search client:only="svelte"></Search>

client:only 在 Astro 里的意思是”这个组件完全跳过服务器渲染”。也就是说,服务器生成的 HTML 里,搜索框的位置只有一个空的占位注释。浏览器拿到 HTML 开始渲染的时候,导航栏右侧是空的。

然后 Svelte 在浏览器里初始化完成,搜索框的完整 DOM 突然出现——一个大约 176px 宽的家伙毫无征兆地插进了 flex justify-between 的布局里。flex 引擎被迫重新分配空间,所有导航链接被挤得集体右移。

等用户看到的时候,Svelte 早已初始化完了,文字也回到了正确的位置。整个过程不到 100 毫秒,但肉眼刚好能捕捉到那个”闪”的瞬间。

修复#

给 Search 组件外面包了一个固定宽度的容器:

<div class="w-11 lg:w-44 h-11 flex items-center justify-end">
<Search client:only="svelte"></Search>
</div>

不管 Svelte 初始化了没有,这个容器的宽度永远是 176px。布局从一开始就是稳定的。

一个宽度的差异,修了好几天。

这篇文章是否对你有帮助?

发现错误或想要改进这篇文章?

在 GitHub 上编辑此页