最稳方式是直接修改有唯一id的link标签href属性,浏览器自动重载CSS且不闪屏;需监听load/error事件确保样式就绪,初始化时应匹配prefers-color-scheme系统偏好。

直接改 link 的 href 属性最稳
浏览器原生支持运行时切换 CSS,只要操作已存在的 <link rel="stylesheet"> 标签的 href 属性即可。它不会触发重请求(如果缓存命中),也不会造成 FOUC,比反复增删 DOM 安全得多。
关键前提:HTML 中必须提前声明一个带唯一 id 的 link 标签,比如:
<link id="theme-css" rel="stylesheet" href="theme-light.css">
后续 JS 只需:
document.getElementById('theme-css').href = 'theme-dark.css'- 新路径必须可访问,且服务端需返回正确 MIME 类型(
text/css) - 跨域时服务端要配好 CORS 响应头,否则控制台报
Cross-Origin Read Blocking (CORB)
必须监听 load 和 error 事件
改完 href 不代表样式立刻生效——CSS 文件还在加载中。此时读 getComputedStyle 或触发动画,大概率拿到旧值。
正确做法是等 load 事件再执行依赖新主题的逻辑:
themeLink.addEventListener('load', () => { localStorage.setItem('theme', 'dark'); })themeLink.addEventListener('error', () => console.error('主题 CSS 加载失败'))- 不要用
setTimeout模拟等待,网络抖动下不可靠 -
load只表示 CSS 文件加载完成,不保证字体、图片等子资源就绪
初始化时要尊重 prefers-color-scheme
用户首次访问,不该硬塞默认主题。应该用 window.matchMedia('(prefers-color-scheme: dark)') 获取系统偏好,并据此设置初始 href。
更进一步,可以监听媒体查询变化,自动响应系统级深色/浅色切换:
const mql = window.matchMedia('(prefers-color-scheme: dark)');mql.addEventListener('change', e => { if (e.matches) themeLink.href = 'theme-dark.css'; })- 注意:这个监听只对系统偏好变化有效,不影响用户手动点“切蓝色主题”这类操作
别碰 import('./x.css') 动态语法
在 Vite 或 Webpack 里写 await import('./dark.css') 看似简洁,但实际几乎没用:
- Webpack 默认会报错或静默失败,除非你额外配
resolve.extensions和 CSS rule - Vite 虽能解析,但返回的是空模块(
{ default: undefined }),CSS 根本不会注入页面 - 根本原因是:动态
import()是 JS 运行时行为,而 CSS 注入依赖构建时的 loader 链(如style-loader) - 真要模块化,得走组件级懒加载路线:
React.lazy(() => import('./Page.module.css'))才会被构建工具识别并注入
load 事件监听。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











