uni-app无法单靠window.matchMedia或uni.getSystemInfoSync().theme实现可靠跨端主题切换,必须组合检测、onShow重读、强制同步及多层适配,否则Android端将卡在启动快照,小程序不支持matchMedia,pages.json变量为静态值且需theme.json配置与原生API同步更新。

uni-app 无法靠 window.matchMedia 或 uni.getSystemInfoSync().theme 单一手段实现可靠、跨平台的系统主题自动切换——必须组合检测 + 主动重读 + 强制同步,否则 Android 小程序和 App 端大概率卡在启动时的主题快照里不动。
为什么 onLaunch 读一次 theme 就失效了
Android 真机上 uni.getSystemInfoSync().theme 只在冷启动或后台唤醒时更新一次,后续系统切深色它不会变;iOS 和 H5 虽能读对,但小程序 WebView 根本不支持 window.matchMedia,直接报 undefined is not a function。你看到“日落到日出”开了却没反应,就是因为只信了 onLaunch 那一次。
- 必须在
App.vue的onShow钩子中重新调用uni.getSystemInfoSync() - 不能只依赖
theme字段,要 fallback 到window.matchMedia?.('(prefers-color-scheme: dark)')?.matches(仅 H5 / 新版小程序有效) - Android App 端可加简单轮询(如每 3 秒查一次),但仅限前台活跃时,避免耗电
pages.json 里 @navBgColor 为啥改了 theme 也不生效
@xxx 变量是编译期/页面首次加载时注入的静态值,运行时改 uni.setStorageSync('theme_mode', 'dark') 完全不影响已渲染的导航栏、tabBar 或安全区背景。这不是 bug,是设计限制。
- 必须配合
uni.reLaunch()或uni.redirectTo()强制刷新当前页,让pages.json重新解析theme.json -
theme.json必须放在项目根目录,且manifest.json中各平台节点(mp-weixin、app-plus、web)都要显式写"darkmode": true和"themeLocation": "theme.json" - tabBar 的
borderStyle在微信小程序里只认"black"/"white"字符串,theme.json里写"#000000"会被截断,别指望传 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这种 class 前缀形式,小程序不支持运行时修改:rootCSS 变量
真正难的不是“怎么切”,而是“什么时候切、在哪切、切完怎么让所有层都跟上”。导航栏、tabBar、安全区、CSS、JS 逻辑(比如 API 请求头)、甚至图标颜色,都要在同一次主题变更中完成同步——漏掉任意一层,用户就会看到错位或闪动。


















