實現方法
方法 | 性能消耗 | 維護成本 | 適用場景 |
---|---|---|---|
內聯樣式 | 較高 | 低 | 小程序 |
CSS變量+屬性選擇器 | 低 | 中 | H5 |
混合方案 | 中等 | 低 | 跨平臺項目 |
優勢特點
- 性能優化:
- H5端使用CSS原生變量切換
- 小程序端使用高效樣式字符串生成
- 切換動畫流暢
- 維護性提升
- 主題配置集中管理
- 新增主題只需要拓展vars對象
使用pinia管理主題
第一步、定義主題和CSS變量
state: () => {return {mode: uni.getStorageSync('theme') || 'light',vars: {light: { '--primary': '#007AFF', '--bg': '#FFFFFF' },dark: { '--primary': '#0BB640', '--bg': '#1A1A1A' },},}
},
第二步、實現主題切換方法
actions: {// 統一切換入口toggle(themeMode) {// 判斷是否傳遞主題,如果沒有則在dark和light之間切換if (themeMode) {this.mode = themeMode} else {this.mode = this.mode === 'light' ? 'dark' : 'light'}// 本地存儲uni.setStorageSync('theme', this.mode)},}
第三步、實現原生組件適配
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根屬性更新
actions: {// H5根屬性更新updateRootAttribute() {const isH5 = process.env.UNI_PLATFORM === 'h5'if (isH5) {// 更新HTML中的data-theme屬性標識document.documentElement.setAttribute('data-theme', this.mode)}},
},
完整代碼
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 H5this.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中使用
/* 多端兼容方案 */
:root {// 默認主題(編譯時注入小程序)--primary: #007aff;--bg: #ffffff;
}// H5動態主題(運行時切換)
@media all {[data-theme='light'] {--primary: #007aff;--bg: #ffffff;}[data-theme='dark'] {--primary: #0bb640;--bg: #1a1a1a;}
}
在App.vue中導入使用
<style lang="scss">
@import '@/styles/index.scss';
</style>
創建一個Hook
通過Hook來管理主題的切換、樣式格式化等,避免重復導入Store
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 }"
,這樣就可以實現在小程序主題切換時變量自動更新
<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文件中添加如下代碼
onLaunch(() => {// 主題初始化const savedTheme = uni.getStorageSync('theme')if (savedTheme) {themeStore.toggle(savedTheme)}
})
優化拓展
添加transition
在css或scss文件中添加如下代碼,使主題切換時更加流暢的過渡,避免生硬切換
* {transition: background-color 0.3s, background 0.3s, color 0.3s;
}
監聽系統主題變化
在App.vue文件中使用uni.onThemeChange
監聽系統主題變化,并同步小程序/H5主題變化
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的主題,如:
state: () => {return {vars: {light: { '--primary': '#007AFF', '--bg': '#FFFFFF' },dark: { '--primary': '#0BB640', '--bg': '#1A1A1A' },red: {// ……新變量}},}
},
// 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
中,這樣可能導致切換主題時不生效