应使用 uni.matchmedia('(prefers-color-scheme: dark)') 检测深色模式,因其跨端稳定;android app 需 fallback,且必须在 onshow 中重读并持久化,同时配置 manifest.json 和 theme.json 才能生效。

不能只靠 uni.getSystemInfoSync().theme 读一次就完事,它在 Android 上大概率是启动快照,且不自动更新。
uni.getSystemInfoSync().theme 在各端表现差异大
这个 API 返回的 theme 字段不是实时监听器,而是平台层“抓拍”的一次快照:
- iOS 和 H5:基本可靠,冷启动时能读对,但切后台再回来不一定刷新
- Android App(尤其真机):多数机型返回
"light",哪怕系统已设深色——它依赖 Android 11+ + HBuilderX 3.6+ 底层桥接,且仅在冷启动或从后台唤醒时触发一次读取 - 微信小程序:部分基础库版本支持,但低版本直接返回
undefined或空对象
真正可用的跨端检测方式是 uni.matchMedia
uni.matchMedia('(prefers-color-scheme: dark)') 是目前唯一被 DCloud 官方确认、在 H5 和新版小程序中稳定可用的方案。注意它和 window.matchMedia 不同,后者在 App 端不可用、小程序里常报错。
- 返回值是对象,需取
.matches属性判断是否深色:const isDark = uni.matchMedia?.('(prefers-color-scheme: dark)')?.matches - H5 和微信/支付宝小程序(基础库 ≥ 2.27.0)可直接用;Android App 端暂不支持,必须 fallback
- 不能在组件
onLoad里调用——App.vue 的onShow才是安全时机,确保页面已激活
为什么 onShow 中重读 theme 是必须动作
用户切到系统设置改深色模式后,App 往往还在后台。等他切回应用时,onShow 是唯一能捕获“这次回来主题可能变了”的钩子。
- 只在
onLaunch读一次,等于放弃所有后台唤醒场景 - 必须配合
uni.setStorageSync('theme_mode', isDark ? 'dark' : 'light')持久化,否则手动切换或重启后丢失 - 读完立刻赋值给
this.themeClass并触发this.$forceUpdate()或uni.$emit('themeChange'),否则根节点 class 不会同步
别漏掉 manifest.json 和 theme.json 的联动配置
即使 JS 层检测对了,如果原生容器没开暗色通道,pages.json 里的 @navBgColor 依然不会生效。
-
manifest.json中每个平台(mp-weixin、app-plus、h5)都得显式写:"darkmode": true和"themeLocation": "theme.json" -
theme.json必须放在项目根目录,结构严格为{"light": {}, "dark": {}},键名大小写敏感 - 漏配任意一项,iOS/H5/小程序都会静默失败,界面毫无变化,控制台也不报错
最易忽略的是:检测逻辑和原生配置必须同时到位。只做 JS 切换,tabBar 和导航栏颜色不会变;只配 theme.json,页面内容样式又不会响应。二者缺一不可。











