监听 prefers-color-scheme 必须用 addEventListener('change'),仅读取 window.matchMedia('(prefers-color-scheme: dark)').matches 得到的是快照值,无法响应系统主题切换;正确做法是立即绑定 change 事件监听器并在回调中读取 e.matches。

监听 prefers-color-scheme 必须用 addEventListener('change')
只读一次 window.matchMedia('(prefers-color-scheme: dark)').matches 拿到的是快照值,系统切换主题时页面完全无反应。必须显式绑定事件监听器,否则「自动切换」就是空谈。
常见错误包括:在组件 mount 时读取一次就结束、把监听逻辑写在按钮点击里、或者误以为 matches 是响应式属性。
- 正确做法是立即调用
mediaQuery.addEventListener('change', handler) - handler 内部应重新读取
e.matches,而不是复用初始值 - 旧版 Safari(addListener,但现代项目可只用标准
addEventListener
favicon 切换不能只改 href,Safari 需强制重置 rel
多数浏览器改 link[rel="icon"].href 就能生效,但 Safari(尤其 iOS)常缓存旧图标,甚至忽略更新。仅改 href 在它身上大概率失效。
可靠方案是移除原 <link> 节点,再插入新节点:
function updateFavicon(isDark) {
const oldLink = document.querySelector("link[rel='icon']");
if (oldLink) oldLink.remove();
const newLink = document.createElement('link');
newLink.rel = 'icon';
newLink.type = 'image/png';
newLink.href = isDark ? '/favicon-dark.png' : '/favicon-light.png';
document.head.appendChild(newLink);
}
- 务必使用绝对路径或根路径(如
/favicon-dark.png),避免相对路径解析错乱 - 加时间戳破缓存(如
?v=165234)对某些 CDN 有用,但不是根本解法 - PWA 场景下,
manifest.json中的icons数组也得配两套,否则添加到主屏幕后不随系统变
内联 SVG 图标必须剥离内联颜色,靠 CSS 变量驱动
如果 SVG 是通过 <img src="icon.svg"> 引入的,window.matchMedia 完全无法控制其颜色——它只是个外部资源,CSS 作用不到内部元素。
真正可控的方式是内联 SVG,并清除所有 fill、stroke 等硬编码颜色属性:
- 保留结构,删掉所有
fill="#333"、stroke="currentColor"这类属性 - 统一用 CSS 自定义属性:
svg path { fill: var(--icon-fill); } -
:root定义默认值,@media (prefers-color-scheme: dark)覆盖变量
注意:不要同时监听 light 和 dark 两个查询,查一个取反即可,避免因浏览器实现差异导致状态冲突。
多个图标类型要统一管理,别让 matchMedia 实例泛滥
一套完整图标集通常含 favicon、apple-touch-icon、manifest icons、内联 SVG、甚至 Open Graph 图片。若每个都单独调用 window.matchMedia,会创建多个 MediaQueryList 实例,增加内存泄漏风险,且难以同步状态。
推荐做法是封装一个单例状态管理:
- 只调用一次
window.matchMedia('(prefers-color-scheme: dark)'),缓存该实例 - 所有图标更新逻辑都订阅同一个
change事件 - 组件卸载时统一清理:调用
mql.removeEventListener('change', handler) - SSR 环境中必须包裹
if (typeof window !== 'undefined'),否则 Next.js 或 Vue SSR 会报错
最易被忽略的是「首次加载时未应用匹配图标」——监听器只响应后续变化,updateFavicon(mql.matches) 这一行漏掉,用户打开页面永远看到默认图标。











