小程序主题切换必须使用CSS变量而非SCSS变量,因后者编译后固化无法运行时修改;所有主题色需定义在:root下并用var()引用,配合uni.onThemeChange监听、storage持久化及原生API同步更新tabBar等元素。

小程序端主题切换必须用 CSS 变量,不是 SCSS 变量
SCSS 变量在编译期就固化成静态 CSS,运行时无法被 JavaScript 修改。小程序里真正能动态改的只有 css 自定义属性(即 --xxx 形式)。哪怕你用 uni.scss 定义了 $uni-color-primary,它最终输出的仍是固定值,换肤时不会变。
常见错误现象:改了 uni.scss 里的颜色变量,重新编译后生效,但运行时调用 uni.setStorageSync('theme', 'dark') 后页面毫无反应——因为没动到 CSS 变量。
- 所有主题色必须声明在
:root或page级选择器下,例如::root { --primary-color: #007aff; } - 组件内统一用
color: var(--primary-color),别写死color: #007aff - 微信小程序要求基础库 ≥ 2.7.0 才支持
var()运行时更新,低于此版本会 fallback 到初始值
主题配置要拆成 JS 对象,不能只靠 pages.json
pages.json 的 style 节点只能设静态背景色或导航栏色,没法响应主题变化。比如你想让暗黑模式下 tabbar 图标变成白色,pages.json 里写死的 iconPath 不会自动替换。
实操建议:
- 把主题配置抽成纯 JS 对象,例如
themes.dark包含{ '--bg-color': '#121212', navBg: '#1e1e1e', tabIcon: 'home_dark.png' } - 图标资源按命名规范存放在
static/tabbar/,如home_dark.png、user_light_sel.png - 切换时调用
uni.setTabBarStyle和uni.setTabBarItem主动更新原生 tabBar,否则只改 CSS 变量,tabbar 还是旧颜色
监听主题变化要用 uni.onThemeChange,不是 watch
小程序平台会主动上报系统级主题变更(如用户开启 iOS 暗黑模式),uni-app 提供了 uni.onThemeChange API 来捕获这个事件。Vue 的 watch 在小程序端对全局状态响应不及时,尤其跨页面时容易漏触发。
关键细节:
- 必须在
App.vue的onLaunch和onShow中都注册监听,冷启动和后台切前台都要覆盖 - 收到回调后,先更新
document.documentElement.style.setProperty,再调用原生 API(如uni.setNavigationBarColor) - 不要在
watch里直接改style绑定,小程序对运行时:style更新有延迟,且部分组件(如uni-button)不支持
首次加载必须从 storage 读取,否则冷启动丢失主题
小程序冷启动时,App.vue 的 onLaunch 先执行,但此时 Vue 实例还没挂载,vuex 或 globalData 都未初始化完毕。如果只依赖 store 初始化主题,用户上次选的暗黑模式会在重启后回到默认亮色。
正确做法:
- 在
App.vue的onLaunch里立即执行uni.getStorageSync('theme'),拿到值后立刻写入document.documentElement.style - 再把该值同步给
store或getApp().globalData.theme,保证后续逻辑一致 - 换肤函数里必须同时调用
uni.setStorageSync和document.documentElement.style.setProperty,缺一不可
最易被忽略的一点:微信小程序的 tabBar 图标路径不支持表达式绑定,必须用 uni.setTabBarItem 显式设置,否则即使 CSS 变量变了,图标还是旧的。


















