必须在css解析前通过内联iife脚本设置data-theme,否则首屏渲染会用默认主题导致fouc;脚本需置于最前、所有css之前,用setattribute而非dataset且不可异步。

主题初始化时闪一下(FOUC),不是JS慢,而是浏览器渲染顺序没抢赢——必须在CSS解析前就设好 data-theme。
为什么页面总先白一下再变深色
浏览器加载顺序是:解析HTML → 加载并解析CSS(此时 :root 用默认变量)→ 渲染首屏 → 执行JS → 读 localStorage → 修改 document.documentElement.dataset.theme → 重绘。这中间的“首屏渲染”就是白屏/浅色闪现的根源。
- 哪怕JS写在
里,只要没内联、没立即执行,就晚于CSS解析 -
DOMContentLoaded或load事件触发时,页面早已渲染过至少一次 - 服务端渲染或预设
<style></style>不现实——静态站点、CDN托管、无后端场景下不可行
必须用 IIFE 内联脚本抢在CSS前执行
把设置 data-theme 的逻辑塞进 里的内联 <script></script>,且必须自执行、无依赖、不等待任何事件。
- 脚本必须放在所有
<link rel="stylesheet">之前,越靠前越好 - 不能用
async或defer,会破坏执行时机 - 示例代码要精简到一行核心逻辑:
document.documentElement.setAttribute('data-theme', localStorage.getItem('theme') || 'light'); - 别用
dataset.theme = ...——某些旧版 Safari 对 dataset 初始化支持不稳定,setAttribute更稳妥
HTML结构中脚本位置决定成败
以下结构是唯一能稳压住FOUC的写法:
<script>(function(){const t=localStorage.getItem('theme');if(t)document.documentElement.setAttribute('data-theme',t);})()</script><link rel="stylesheet" href="theme.css">
- 这个
<script></script>必须紧贴开始,不能有空行或注释挡路 - 如果用了构建工具(Vite、Webpack),确保该脚本不被移除、不被异步化、不被包裹进模块作用域
- 若用 Vue/React,不要把 theme 设置逻辑写在组件
mounted或useEffect里——那已经太晚了
最易被忽略的点:很多人以为“JS设置了 theme 就万事大吉”,却没意识到浏览器根本不关心你 JS 多快,只认 HTML 解析流中的执行节点。抢不到那个毫秒级窗口,FOUC 就永远存在。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











