很多項目中有些會涉及到明暗主題切換的功能,今天就來梳理一下有關這方面的具體實現步驟和使用到的方法,本篇文章使用到的組件是tdesign,利用部分案例,主要闡述明暗主題切換的技術原理和實現步驟,僅供參考。
目錄
一、技術原理與核心
二、實現步驟詳解
1.組件搭建
2.狀態持久化
?3.模式切換邏輯
?4.組件掛載
5.事件監聽
?6.配置與優化
7.使用
三、全部代碼
一、技術原理與核心
TDesign 通過 CSS 自定義屬性(變量)實現主題切換,所有顏色相關配置均使用?var()
?函數引用變量。現代瀏覽器已廣泛支持該特性,TDesign 默認提供淺色/深色兩套色板。主要核心邏輯是通過操作?html
?標簽的?theme-mode
?屬性觸發樣式切換,TDesign 組件庫會自動監聽該屬性變化并應用對應主題樣式。
二、實現步驟詳解
具體步驟這里省略了搭建框架和項目中安裝tdesign組件,主要詳解明暗主題切換的具體代碼和邏輯。
1.組件搭建
一般的明暗切換為一個太陽和一個月亮圖標的切換,這里我們借用tdesign的icon和按鈕來搭建組件
<template><div class="mode-btn-container"><t-button @click="toggleMode" class="mode-btn"><template #icon><ModeDarkIcon v-if="isDarkMode" /><ModeLightIcon v-else /></template></t-button></div>
</template><script setup>
import { ref } from 'vue'
const isDarkMode = ref(false)
const toggleMode = () => {}
</script><style lang="less" scoped>
.mode-btn-container {.mode-btn {margin-right: 0.5vw;}
}
</style>
2.狀態持久化
使用?useStorage
?組合式函數(通常來自?@vueuse/core
?庫),創建響應式狀態?isDarkMode
,值會持久化存儲在 localStorage,鍵名為?'theme-mode'
,這也是tdesign組件中的主題,默認值為?false
(初始為淺色模式),當值變化時會自動同步到本地存儲,頁面刷新后仍可保持狀態
import { useStorage } from '@vueuse/core'
const isDarkMode = useStorage('theme-mode', false)
?3.模式切換邏輯
const toggleMode = () => {isDarkMode.value = !isDarkMode.value // 切換布爾值document.documentElement.setAttribute('theme-mode', isDarkMode.value ? 'dark' : 'light' // 更新HTML根元素屬性)emit('toggleMode', isDarkMode.value) // 觸發自定義事件
}
?4.組件掛載
初始化加載時同步主題狀態(比如從localStorage恢復的持久化狀),確保首次渲染時應用正確的主題樣式
onMounted(() => {document.documentElement.setAttribute('theme-mode', isDarkMode.value ? 'dark' : 'light')
})
5.事件監聽
監聽isDarkMode
響應式變量的變化,當主題狀態變化時(通過切換操作),實時更新DOM屬性,
watch(isDarkMode, newVal => {document.documentElement.setAttribute('theme-mode', newVal ? 'dark' : 'light')
})
?6.配置與優化
寫完以上代碼之后其實我們發現還未達到想要的想要,想要更好的使用還需要最后一步,也就是在使用:root選擇器定義全局變量
//該代碼依據項目進行修改,僅供參考
:root[theme-mode="light"] {--color-background: #009994;--color-title-background: #71b2b1;--color-chart-background: rgba(255, 255, 255, 0.7);--color-title-text: #282727;--color-manage-background: #fff;--color-dialog-txt:#282727;--color-background-img: url('@/assets/images/page/net-frame.gif') no-repeat;--color-detail-txt:#282727;--color-background-net:#009994;--color-txt-net:#fff;--color-station-background:#f2fffe;--bg-date-picker: #fff;--td-bg-color-secondarycontainer:#f3f3f3;--ele-hover-bg-color:#afe2f6;--ele-bg-color:#fff;:root[theme-mode="dark"] {--color-background: #02455f;--color-title-background: #116e93;--color-chart-background: #7c8d8d31;--color-title-text: #fff;--color-manage-background: #0F1014;--color-dialog-txt:#282727;--color-background-img: url('@/assets/images/page/net-frame-dark.gif') no-repeat;--color-detail-txt:#fff;--color-background-net:#fff;--color-txt-net:#282727;--color-station-background:none;--bg-date-picker: #fff;--td-bg-color-secondarycontainer:#c0bebe;--ele-hover-bg-color:#51a0be;--ele-bg-color:#242424;}
7.使用
具體使用方法對顏色使用?var()
?函數引用變量,具體如下,假設我想要對這個切換的按鈕實現明暗背景顏色的變化
.mode-btn {background: var(--color-background);margin-right: 0.5vw;}
三、全部代碼
<template><div class="mode-btn-container"><t-button @click="toggleMode" class="mode-btn"><template #icon><ModeDarkIcon v-if="isDarkMode" /><ModeLightIcon v-else /></template></t-button></div>
</template><script setup>
import { ref } from 'vue'
import { useStorage } from '@vueuse/core'
// 主題狀態管理
const isDarkMode = useStorage('theme-mode', false)
const toggleMode = () => {isDarkMode.value = !isDarkMode.valuedocument.documentElement.setAttribute('theme-mode', isDarkMode.value ? 'dark' : 'light')emit('toggleMode', isDarkMode.value)
}
onMounted(() => {document.documentElement.setAttribute('theme-mode', isDarkMode.value ? 'dark' : 'light')
})
watch(isDarkMode, newVal => {document.documentElement.setAttribute('theme-mode', newVal ? 'dark' : 'light')
})
</script><style lang="less" scoped>
.mode-btn-container {.mode-btn {background: var(--color-background);margin-right: 0.5vw;}
}
</style>
全局css的文件,我這里是common.less
//該代碼依據項目進行修改,僅供參考
:root[theme-mode="light"] {--color-background: #009994;--color-title-background: #71b2b1;--color-chart-background: rgba(255, 255, 255, 0.7);--color-title-text: #282727;--color-manage-background: #fff;--color-dialog-txt:#282727;--color-background-img: url('@/assets/images/page/net-frame.gif') no-repeat;--color-detail-txt:#282727;--color-background-net:#009994;--color-txt-net:#fff;--color-station-background:#f2fffe;--bg-date-picker: #fff;--td-bg-color-secondarycontainer:#f3f3f3;--ele-hover-bg-color:#afe2f6;--ele-bg-color:#fff;:root[theme-mode="dark"] {--color-background: #02455f;--color-title-background: #116e93;--color-chart-background: #7c8d8d31;--color-title-text: #fff;--color-manage-background: #0F1014;--color-dialog-txt:#282727;--color-background-img: url('@/assets/images/page/net-frame-dark.gif') no-repeat;--color-detail-txt:#fff;--color-background-net:#fff;--color-txt-net:#282727;--color-station-background:none;--bg-date-picker: #fff;--td-bg-color-secondarycontainer:#c0bebe;--ele-hover-bg-color:#51a0be;--ele-bg-color:#242424;}