必须用 pinia + pinia-plugin-unistorage + scss 变量注入三者联动;小程序不支持 css 自定义属性,需通过 class 切换(如 .theme-dark)触发预编译的 scss 主题样式,配合持久化 store 确保跨页面/后台恢复一致。

必须用 Pinia + pinia-plugin-unistorage + SCSS 变量注入三者联动,缺一不可;直接改 CSS 变量或只存 JS 状态都会在小程序里断连或不生效。
为什么 uni.setStorageSync 存主题色没用?
小程序渲染层和逻辑层是分离的:JS 里改了 uni.setStorageSync('theme', 'dark'),但 WXML 和 WXSS 不会自动响应。WXSS 不支持 JS 动态注入变量,:root { --primary-color: #xxx } 在小程序里根本不会被识别——微信原生不支持 CSS 自定义属性(CSS Custom Properties)在 WXSS 中运行时生效。
常见错误现象:
• 主题切换后按钮颜色没变,但 console.log 显示 store 里值已更新
• 切后台再切回来,主题恢复成默认值(因为样式没持久化,只存了 JS 状态)
• H5 正常,小程序白屏或样式错乱(跨端差异暴露)
- SCSS 变量必须在编译期注入,靠
uni.scss全局生效 - 运行时切换主题,本质是「换一套预编译好的 SCSS 类名」+「同步更新存储」
- 不能依赖
document.documentElement.style.setProperty,小程序里无document
如何让 SCSS 主题变量真正动态生效?
核心思路:把主题当成一个「类名开关」,所有主题相关样式都包裹在 .theme-light / .theme-dark 下,通过切换 <view class="theme-dark"></view> 的根 class 控制样式流。
实操步骤:
- 在
uni.scss里定义两套变量,例如:$theme-light-primary: #409EFF、$theme-dark-primary: #5FB878 - 在
src/styles/themes/light.scss和src/styles/themes/dark.scss中,用@each或@mixin生成对应主题的 CSS 规则,全部加前缀:.theme-light .u-button、.theme-dark .u-button - 主 App.vue 的根节点绑定:
<view :class="['theme-' + $store.theme.current]"></view> - 确保所有页面/组件的样式都写在 scoped style 里,或显式使用
.theme-light前缀,避免全局污染
⚠️ 注意:uni.scss 本身不能动态重载,它只是编译时入口;真正的「动态」靠的是 class 切换触发已编译好的 CSS 规则匹配。
Pinia 主题 store 怎么配持久化?
必须用 pinia-plugin-unistorage,不是 pinia-plugin-persistedstate —— 后者在小程序里调用 localStorage 会报错。
store 定义示例(src/stores/theme.ts):
import { defineStore } from 'pinia'
export const useThemeStore = defineStore('theme', {
state: () => ({
current: 'light' as 'light' | 'dark',
// 其他主题字段...
}),
persist: {
key: 'uni_theme_config',
storage: {
getItem: (key) => uni.getStorageSync(key),
setItem: (key, value) => uni.setStorageSync(key, value),
removeItem: (key) => uni.removeStorageSync(key)
}
}
})
关键点:
-
persist必须显式声明,且key建议带前缀避免和其他 store 冲突 - 不要在
state里存函数或 DOM 引用,否则序列化失败 - 初始化时,
current应从uni.getStorageSync读一次兜底,防止首次加载闪动 - 切换主题后立即调用
store.$patch({ current: 'dark' }),插件会自动落盘
小程序真机上主题切换卡顿或失效?检查这三点
这是最常被忽略的执行链断裂点:
-
App.vue 的 class 绑定没生效:确认
$store.theme.current是响应式读取,不是字符串字面量;setup()里要用useThemeStore()而非直接 import store 实例 -
WXSS 编译未包含主题文件:检查
style lang="scss"节点是否@import了../styles/themes/light.scss和dark.scss;uni-app 的 scss 编译不支持条件 import,必须全量引入 -
页面生命周期干扰:在
onLoad或onShow里手动调uni.setNavigationBarColor改状态栏,但忘记同步更新 store,导致下次进入页面时状态不一致
复杂点在于:主题配置要同时驱动三件事——UI 样式、导航栏颜色、图标 SVG fill 属性。这三者更新时机不同、API 不同、持久化方式也不同,必须拆开管理,不能塞进同一个 store 字段里硬同步。











