实现方法
方法 | 性能消耗 | 维护成本 | 适用场景 |
---|---|---|---|
内联样式 | 较高 | 低 | 小程序 |
CSS变量+属性选择器 | 低 | 中 | H5 |
混合方案 | 中等 | 低 | 跨平台项目 |
优势特点
- 性能优化:
- H5端使用CSS原生变量切换
- 小程序端使用高效样式字符串生成
- 切换动画流畅
- 维护性提升
- 主题配置集中管理
- 新增主题只需要拓展vars对象
使用pinia管理主题
第一步、定义主题和CSS变量
javascript
state: () => {
return {
mode: uni.getStorageSync('theme') || 'light',
vars: {
light: { '--primary': '#007AFF', '--bg': '#FFFFFF' },
dark: { '--primary': '#0BB640', '--bg': '#1A1A1A' },
},
}
},
第二步、实现主题切换方法
javascript
actions: {
// 统一切换入口
toggle(themeMode) {
// 判断是否传递主题,如果没有则在dark和light之间切换
if (themeMode) {
this.mode = themeMode
} else {
this.mode = this.mode === 'light' ? 'dark' : 'light'
}
// 本地存储
uni.setStorageSync('theme', this.mode)
},
}
第三步、实现原生组件适配
javascript
actions: {
// 原生组件适配
updateNative() {
// 判断终端类型
const isMP = process.env.UNI_PLATFORM?.startsWith('mp-')
const vars = this.vars[this.mode]
if (isMP) {
// 设置顶部导航栏样式
uni.setNavigationBarColor({
backgroundColor: vars['--bg'],
frontColor: this.mode === 'dark' ? '#ffffff' : '#000000',
})
// code......
}
},
}
第四步、H5根属性更新
javascript
actions: {
// H5根属性更新
updateRootAttribute() {
const isH5 = process.env.UNI_PLATFORM === 'h5'
if (isH5) {
// 更新HTML中的data-theme属性标识
document.documentElement.setAttribute('data-theme', this.mode)
}
},
},
完整代码
javascript
import { defineStore } from 'pinia'
export const useThemeStore = defineStore('theme', {
state: () => {
return {
mode: uni.getStorageSync('theme') || 'light',
vars: {
light: { '--primary': '#007AFF', '--bg': '#FFFFFF' },
dark: { '--primary': '#0BB640', '--bg': '#1A1A1A' },
},
}
},
actions: {
// 统一切换入口
toggle(themeMode) {
if (themeMode) {
this.mode = themeMode
} else {
this.mode = this.mode === 'light' ? 'dark' : 'light'
}
uni.setStorageSync('theme', this.mode)
// 多端样式更新
this.updateNative()
// #ifdef H5
this.updateRootAttribute()
// #endif
},
// 原生组件适配
updateNative() {
const isMP = process.env.UNI_PLATFORM?.startsWith('mp-')
const vars = this.vars[this.mode]
if (isMP) {
uni.setNavigationBarColor({
backgroundColor: vars['--bg'],
frontColor: this.mode === 'dark' ? '#ffffff' : '#000000',
})
}
},
// H5根属性更新
updateRootAttribute() {
const isH5 = process.env.UNI_PLATFORM === 'h5'
if (isH5) {
document.documentElement.setAttribute('data-theme', this.mode)
}
},
},
})
定义CSS变量
定义CSS变量,H5中使用
plain
/* 多端兼容方案 */
:root {
// 默认主题(编译时注入小程序)
--primary: #007aff;
--bg: #ffffff;
}
// H5动态主题(运行时切换)
@media all {
[data-theme='light'] {
--primary: #007aff;
--bg: #ffffff;
}
[data-theme='dark'] {
--primary: #0bb640;
--bg: #1a1a1a;
}
}
在App.vue中导入使用
vue
<style lang="scss">
@import '@/styles/index.scss';
</style>
创建一个Hook
通过Hook来管理主题的切换、样式格式化等,避免重复导入Store
javascript
import { computed } from 'vue'
import { useThemeStore } from '@/stores/theme'
export const useTheme = () => {
const themeStore = useThemeStore()
// 响应式主题变量
const themeVars = computed(() => {
const result = {
// 小程序端需要转换的样式
mpStyle: null,
// H5数据属性
dataTheme: themeStore.mode,
}
if (isMP.value) {
result.mpStyle = Object.entries(themeStore.vars[themeStore.mode])
.map(([k, v]) => `${k}:${v}`)
.join(';')
}
return result
})
const isMP = computed(() => process.env.UNI_PLATFORM?.startsWith('mp-'))
return {
isMP,
toggle: themeStore.toggle, // 切换方法
currentMode: computed(() => themeStore.mode), // 当前模式
themeVars, // 样式绑定对象
}
}
在组件中使用
最主要的代码是::style="{ ...themeVars.mpStyle }"
,这样就可以实现在小程序主题切换时变量自动更新
vue
<template>
<view class="container" :style="{ ...themeVars.mpStyle }">
<view class="w-150px box">
<text>主题切换测试</text>
<text class="iconfont icon-sousuo"></text>
<button type="primary" @tap="() => toggle()">切换主题</button>
</view>
</view>
</template>
<script setup>
import { useTheme } from '@/Hooks/useTheme'
const { themeVars, toggle } = useTheme()
</script>
<style lang="scss" scoped>
.container {
.box {
background-color: var(--bg);
font-size: 18px;
font-weight: 500;
line-height: 32px;
color: var(--primary);
}
}
</style>
主题初始化
在App.vue文件中添加如下代码
vue
onLaunch(() => {
// 主题初始化
const savedTheme = uni.getStorageSync('theme')
if (savedTheme) {
themeStore.toggle(savedTheme)
}
})
优化拓展
添加transition
在css或scss文件中添加如下代码,使主题切换时更加流畅的过渡,避免生硬切换
plain
* {
transition: background-color 0.3s, background 0.3s, color 0.3s;
}
监听系统主题变化
在App.vue文件中使用uni.onThemeChange
监听系统主题变化,并同步小程序/H5主题变化
vue
onLaunch(() => {
// 监听系统主题变化
uni.onThemeChange(({ theme }) => {
const systemTheme = theme === 'dark' ? 'dark' : 'light'
themeStore.toggle(systemTheme)
})
})
新增主题?
如果想要新增主题,只需要在stores/theme.js
和style/index.scss
文件中添加对应主题的CSS变量,theme.js中定义小程序的主题,index.scss定义H5的主题,如:
vue
state: () => {
return {
vars: {
light: { '--primary': '#007AFF', '--bg': '#FFFFFF' },
dark: { '--primary': '#0BB640', '--bg': '#1A1A1A' },
red: {
// ......新变量
}
},
}
},
css
// H5动态主题(运行时切换)
@media all {
[data-theme='light'] {
--primary: #007aff;
--bg: #ffffff;
}
[data-theme='dark'] {
--primary: #0bb640;
--bg: #1a1a1a;
}
[data-theme='red'] {
/* ......新变量 */
}
}
注意事项
:root
选择器:用于匹配文档的根元素(在 HTML 文档中即 标签)。它是定义全局 CSS 变量的最佳位置,尤其在主题切换场景中发挥关键作用。@media all
媒体查询:所有设备上都生效- 请不要将
index.scss
中的代码放到uni.scss
中,这样可能导致切换主题时不生效