不必用Vuex或$u.store;最轻量方式是uni.setStorageSync持久化主题+App.vue的onLaunch/onShow注入class,H5用document.documentElement.style.setProperty更新CSS变量,小程序/App用预设class切换,配合computed读取主题值动态绑定样式。

主题颜色切换必须用 Vuex 或 $u.store 吗?
不是。uni-app 中主题切换最轻量、最可控的方式是用 uni.setStorageSync + App.vue 的 onLaunch 和 onShow 做全局样式注入,配合 uni.addStyle(H5)或动态 class(App/小程序)。Vuex 会增加不必要的响应式开销,尤其只改颜色变量时。
关键点:主题色本质是 CSS 变量(:root),不是状态数据。直接操作样式表比驱动整个 store 更快、更稳定。
- 小程序不支持动态修改
:root,需用class切换预设样式块(如theme-dark) - H5 可用
document.documentElement.style.setProperty实时更新 CSS 变量 - App 端(iOS/Android)建议统一走 class 方案,避免原生渲染层样式缓存问题
如何在 App.vue 中注入主题 class 并持久化选择?
在 App.vue 的 onLaunch 里读取本地存储的主题标识,然后给 <body>(H5)或根 <view>(小程序/App)加对应 class;同时监听 onShow 防止后台切回时失效。
// App.vue
export default {
onLaunch() {
const theme = uni.getStorageSync('theme') || 'light'
this.setThemeClass(theme)
},
onShow() {
const theme = uni.getStorageSync('theme') || 'light'
this.setThemeClass(theme)
},
methods: {
setThemeClass(theme) {
// H5:操作 body class
if (process.env.UNI_PLATFORM === 'h5') {
document.body.className = `theme-${theme}`
}
// 小程序/App:通过 $scope.globalData 或 provide/inject 透传,但更推荐页面级 class 绑定
// 此处可触发全局事件通知子组件重绘
uni.$emit('themeChanged', theme)
}
}
}
注意:uni.setStorageSync('theme', 'dark') 必须在用户点击后立刻执行,否则切页再回来就丢失了。
页面中怎么响应主题变化并避免重复渲染?
不要在每个页面的 data 里存一份 theme,而是用 computed 直接读取存储值,或监听全局事件。否则多个页面同时响应会造成样式抖动。
- 推荐写法:在页面
computed中返回uni.getStorageSync('theme'),配合v-bind:class动态绑定 - 避免写法:在
data初始化 theme,再用watch监听uni.$on('themeChanged')—— 容易漏掉onUnload解绑,导致内存泄漏 - 样式文件里提前写好两套规则,比如
.theme-dark .btn { background: #333; }和.theme-light .btn { background: #fff; }
示例片段:
<template>
<view class="btn" :class="'theme-' + currentTheme">按钮</view>
</template>
<script>
export default {
computed: {
currentTheme() {
return uni.getStorageSync('theme') || 'light'
}
}
}
</script>
为什么 uni.setStorageSync 比 uni.setStorage 更适合存主题?
因为主题切换是高频、低容量、强一致性要求的操作。uni.setStorageSync 是同步阻塞调用,确保写入完成才执行下一步(比如跳转或关闭弹窗),而 uni.setStorage 异步回调可能被中断或延迟,造成「点了暗色模式,页面没变,再点一次才生效」的现象。
- 主题值只有字符串(
'light'/'dark'),体积远小于 1KB,不存在同步卡顿风险 - 所有平台(微信小程序、支付宝、H5、App)都 100% 支持
uni.setStorageSync - 如果用了
uni.setStorage,又没处理fail回调,主题就静默失败,用户完全无感知
真正容易被忽略的是:切换主题后,必须主动触发一次页面重绘(比如 uni.$emit 或 this.$forceUpdate()),否则已挂载的组件不会响应 class 变化 —— 这不是 Vue 响应式的问题,是 CSS 层面的 class 替换需要 DOM 重新计算样式。


















