实现思路整理
1.将主题通用的全局变量定义在一个sass文件中,然后在vite中配置自动导入
2.创建多个主题json文件,key与全局变量一致,值就用对应的样式代码,例如颜色、字体样式,边框样式等等
3.项目初始化的时候从本地缓存中读取当前的主题,然后读取主题json文件,遍历覆盖相同的主题变量的值
4.主题切换组件在主题切换时切换主题
实现步骤
1.定义全局主题样式变量
首先我们在src目录下创建一个styles目录,里面创建一个common.scss文件,里面定义一些通用的样式变量,如下:
- 该文件中的变量值咱们可以随便取,因为真实的值会在初始化时通过主题json文件进行覆盖,该文件仅仅是作为变量声明,方便我们在样式代码中调用
- 如果使用了element这类主题框架,咱们可以通过命名同样的名字来覆盖框架的默认颜色
- src\styles\common.scss
/**全局SCSS变量 样式值以同文件夹下的json主题配置的值为主,命名推荐全拼小驼峰,这里定义的值可随意取*/
$backgroundColor: var(--background-color, #181616);
$fontColor: var(--font-color, #f41717);
$borderStyle:var(--border-style,1px solid #ccf547);
$linkColor: var(--link-color, skyblue);
// 覆盖element的颜色配置 这里配置的颜色不会覆盖,初始化的时候通过代码进行覆盖
$primaryColor: var(--el-color-primary, #fe587f);
$textColor: var(--el-text-color, #232332);
(1).我们在vite.config.ts文件中设置默认导入sass
export default defineConfig({
...
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/styles/common.scss" as *;` // 引入scss变量
}
}
},
...
})
(2).在vue组件中使用scss变量
#app-container{
background-color: $backgroundColor;
color: $fontColor;
}
2.定义主题模板
我们在styles目录下新建一个json目录,里面定义两个主题模板,分别命名theme-dark.json、theme-light.json:
key必须和variables中的var命名的全局名称一致,值就是当前主题设置的样式
theme-light.json:
{
"--background-color": "#fff",
"--font-color": "#333",
"--border-style": "2px dashed #fff",
"--link-color": "#b8bfc6",
"--el-color-primary": "#837d7d",
"--el-color-danger": "#674508"
}
theme-dark.json:
{
"--background-color": "#333",
"--font-color": "#fff",
"--border-style": "1px solid #ccf547",
"--link-color": "skyblue",
"--el-color-primary": "#333",
"--el-color-danger": "#e99c11"
}
3.封装颜色工具类
有了对应的主题文件,我们就需要在项目初始化的时候,使用对应的主题文件覆盖主题变量的值,这里我们把主题相关的操作封装到单独的工具类中,src/utils目录下创建themeUtils.ts,主要实现以下功能:
- 主题颜色初始化:从缓存中获取当前主题,没有取默认值,然后加载对应的json,用json中的值进行覆盖
- 主题颜色切换:颜色切换支持主题切换和自定义颜色切换
- 全局颜色修改:利用sass中定义的全局变量,直接配合document.getElementsByTagName(“body”)[0].style.setProperty(key, value);即可实现动态修改
src\utils\themeUtils.ts
import themeDarkConfig from "@/styles/json/theme-dark.json";
import themeLightConfig from "@/styles/json/theme-light.json";
import {useThemeStore} from '@/store'
/**主题前缀 */
export const keyThemePrefix = "theme_";
/**主题模式 枚举 */
export const enum themeModeEnum {
dark = "dark",
light = "light",
}
/**主题map,方便在主题切换组件中取值 */
export const themeModeMap = new Map<string, string>([
[themeModeEnum.dark, "暗色主题"],
[themeModeEnum.light, "亮色主题"],
])
/**
* 设置样式属性
* @param key 样式名
* @param value 样式值
*/
function setStyleProperty(key: string, value: string) {
document.getElementsByTagName("body")[0].style.setProperty(key, value);
}
/**
* 从缓存或默认json样式配置中全局设置样式变量
*/
export const initTheme = () => {
themeChange(themeModeEnum.light,false);
}
/**
* 切换主题模式
* @param mode 主题模式
* @param electronChange 窗口是否需要同步修改 默认需要
*/
export function themeChange(mode: string, electronChange = true) {
// app状态管理
const themeStore = useThemeStore();
// 设置主题状态
themeStore.changeThemeTitle(mode)
// 取到对应主题的json配置
let themeConfig;
switch (mode) {
case themeModeEnum.dark:
themeConfig = themeDarkConfig;
break;
// 补充任意个自定义主题色.......
default:
themeConfig = themeLightConfig;
break;
}
// 取到所有样式的key 和 value
const styleKeys = Object.keys(themeConfig);
const styleValues = Object.values(themeConfig);
// 遍历设置全局scss变量的样式
for (let i = 0; i < styleKeys.length; i++) {
const styleKey = styleKeys[i];
/// 走缓存或json配置的默认值
const styleValue = styleValues[i];
/// 设置样式
setStyleProperty(styleKey, styleValue as string);
}
}
export default {
themeChange,
initTheme,
setStyleProperty,
keyThemePrefix,
};
4.初始化主题色
import { initTheme } from "@/utils/themeUtils";
// .....
app.mount("#app").$nextTick(() => {
// ......
// 主题色初始化
initTheme();
});
5.主题一键切换组件
<template>
<div>
<el-select v-model="value" class="m-2" placeholder="Select" size="large" @change="changeTheme">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</template>
<script setup>
import { ref } from 'vue'
import {themeChange} from '@/utils/themeUtils'
let value = ref('light')
let options = ref([
{key:'浅色模式',value:'light'},
{key:'暗色模式',value:'dark'}
])
function changeTheme(val){
console.log('val:',val)
themeChange(val)
}
</script>
<style lang="scss" scoped>
</style>