uni-app无法靠window.matchmedia自动响应深色模式切换,因小程序webview不支持该api,返回空对象或报错;ios和h5虽可用但android端不可靠,真正可用的是uni.matchmedia,仅限h5及部分新版小程序基础库。

uni-app 无法靠 window.matchMedia 或系统 API 自动、实时响应深色模式切换,必须手动检测 + 主动同步 + 强制重绘,否则页面会卡在旧主题里不动。
为什么 window.matchMedia('(prefers-color-scheme: dark)') 在小程序里不工作
微信/支付宝等小程序 WebView 不支持标准媒体查询 API,window.matchMedia 要么返回空对象,要么直接报 undefined is not a function。iOS 和 H5 端虽能用,但 Android App 端(尤其低版本)也常 fallback 到不可靠状态。别把它当跨平台主力方案。
- 真正在小程序里可用的只有
uni.matchMedia('(prefers-color-scheme: dark)'),但它仅在 H5 和部分新版小程序基础库中有效,微信基础库 - Android 真机上
uni.getSystemInfoSync().theme是启动快照,系统切暗后它不会更新,哪怕用户开了“日落到日出”自动切换 - 如果你只监听
onLaunch读一次 theme,那 App 在后台被唤醒后仍显示旧主题——必须在onShow中重新读取
如何让 pages.json 里的 @navBgColor 真正生效
@xxx 变量只在编译期或页面首次加载时注入,运行时改 uni.setStorageSync('theme', 'dark') 完全不影响已渲染的导航栏和 tabBar。这不是 bug,是设计限制。
- 必须配合
uni.reLaunch()或uni.redirectTo()强制刷新当前页,才能让pages.json重新读取 theme.json 中对应值 -
theme.json必须放在项目根目录,且manifest.json中各平台节点(如mp-weixin、app-plus)都要显式写"darkmode": true和"themeLocation": "theme.json",漏一个平台就失效 -
tabBar.borderStyle在微信小程序里只认"black"/"white"字符串,theme.json 里写"#000000"会被截断成"black",别指望它能传 hex 值
怎么避免页面闪屏(白/黑屏跳变)
App.vue 渲染早于 onLaunch 执行,如果主题 class 晚绑定,首屏就会先按默认色渲染再闪一下。关键不是“加 class”,而是“什么时候加”。
- 主题存储必须用
uni.setStorageSync('theme_mode', 'dark'),不能只存 Vuex 或内存变量;key 名统一用'theme_mode',大小写敏感 - App.vue 的
onLaunch和onShow都要调用uni.getStorageSync('theme_mode'),并立即设置data.themeClass,确保根<view></view>第一帧就带对的 class - 所有 CSS 必须写成
.theme-dark .header、.theme-light .button这种嵌套形式,禁用:rootCSS 变量做主干切换——小程序不支持 runtime 更新 :root 变量 - H5 端可额外加
document.documentElement.setAttribute('data-theme', theme)做兼容,但只是锦上添花,不能替代 class 方案
手动切换主题时,为什么导航栏颜色没变
因为 pages.json 的全局样式是静态注入的,你改了本地存储,但页面 DOM 已经渲染完成,导航栏组件不会自动 re-init。
- 最轻量解法:在切换按钮里执行
uni.setStorageSync('theme_mode', 'dark')后,立刻uni.$emit('themeChange', 'dark'),让当前页 watch 这个事件并调用this.$forceUpdate()(仅对非 pages.json 控制的区域有效) - 彻底解法:用
uni.reLaunch({ url: getCurrentPages()[0].route })重载当前页,强制 pages.json 重新解析 theme.json - 注意:不要用
uni.navigateTo再开一页,会导致路由栈混乱;uni.redirectTo会丢掉参数,reLaunch是唯一兼顾参数保留和强制刷新的选择
真正麻烦的不是“怎么切”,而是“切完谁来通知导航栏、tabBar、页面组件、图标颜色、API 请求头(比如某些接口要传 theme=dark)一起动”。每个环节都得手动对齐,没有银弹。











