Vue3实战系列 --- 一键换肤

219 阅读1分钟

前言

本系列只讲项目实战,前期搭建请自行参考官方文档

实现一键换肤

基于 css-vars-ponyfill 实现一键换肤功能

先看效果

image.png

image.png

image.png

一、安装依赖

 # yarn add css-vars-ponyfill 或
 npm i css-vars-ponyfill  

二、项目中使用

src/ 创建 theme 目录

创建 src/theme/index.ts

 // theme.js
 import { blueTheme, darkTheme, greenTheme, vermilionTheme } from "./variable";
 import cssVars from "css-vars-ponyfill";
 ​
 // 返回主题配色
 export const getTheme = (theme: string, color: string):any => {
   switch(theme) {
     case "blue":
       blueTheme["--primary-text-color"] = color;
       return blueTheme;
     case "dark":
       darkTheme["--primary-text-color"] = color;
       return darkTheme;
     case "green":
       greenTheme["--primary-text-color"] = color;
       return greenTheme;
     case "vermilion":
       vermilionTheme["--primary-text-color"] = color;
       return vermilionTheme;
   }
 }
 ​
 export const initTheme = (theme: any, color: string) => {
   document.documentElement.setAttribute("data-theme", theme);
   cssVars({
     watch: true, // 当添加,删除或修改其<link>或<style>元素的禁用或href属性时,ponyfill将自行调用
     variables: getTheme(theme, color), // variables 自定义属性名/值对的集合
     onlyLegacy: false, // false  默认将css变量编译为浏览器识别的css样式  true 当浏览器不支持css变量的时候将css变量编译为识别的css
   });
 };

创建 src/theme/variable.ts

我这里是创建了四套样式,可以根据自己的UI作调整

 // 字体变量
 const baseSize = {
   "--font-size-large-x": "22px",
   "--font-size-large": "18px",
   "--font-size-medium": "14px",
   "--font-size-medium-x": "16px",
   "--font-size-small-s": "10px",
   "--font-size-small": "12px",
 };
 ​
 ​
 // 钴蓝
 export const blueTheme = {
   // 菜单栏 颜色
   "--menu-bg": "#2f3447",
   "--logo-bg": "#2f3447",
   "--logo-text-color": "#fff",
   "--menu-actvie-bg": "#1890ff",
   "--menu-hover-bg": "#1890ff",
   "--menu-text-color": "#fff",
   "--menu-active-text-color": "#fff",
   "--menu-hover-text-color": "#fff",
   
   // 顶部导航栏 颜色
   "--header-bg": "#fff",
   "--header-text-color": "#333",
   "--header-hover-text-color": "#1890ff",
 ​
   // 内容区域配色
 ​
 ​
   "--border-color": "#dcdfe6",
   // 主色系
   "--primary-text-color": "#2f3447",
 ​
   ...baseSize
 }
 ​
 // 极黑
 export const darkTheme = {
   // 菜单栏 颜色
   "--menu-bg": "#2f3447",
   "--logo-bg": "#2f3447",
   "--logo-text-color": "#fff",
   "--menu-actvie-bg": "#222222",
   "--menu-hover-bg": "#222222",
   "--menu-text-color": "#eee",
   "--menu-active-text-color": "#eee",
   "--menu-hover-text-color": "#eee",
   
   // 顶部导航栏 颜色
   "--header-bg": "#2f3447",
   "--header-text-color": "#eee",
   "--header-hover-text-color": "#222222",
 ​
   // 内容区域配色
 ​
   "--border-color": "#dcdfe6",
   "--primary-text-color": "#eee",
 ​
   ...baseSize
 }
 ​
 // 果绿
 export const greenTheme = {
   // 菜单栏 颜色
   "--menu-bg": "#fff",
   "--logo-bg": "#51c21a",
   "--logo-text-color": "#fff",
   "--menu-actvie-bg": "#fff",
   "--menu-hover-bg": "#fff",
   "--menu-text-color": "#333",
   "--menu-active-text-color": "#51c21a",
   "--menu-hover-text-color": "#51c21a",
   
   // 顶部导航栏 颜色
   "--header-bg": "#fff",
   "--header-text-color": "#333",
   "--header-hover-text-color": "#333",
   "--primary-text-color": "#51c21a",
 ​
   // 内容区域配色
 ​
   "--border-color": "#dcdfe6",
 ​
   ...baseSize
 }
 ​
 // 朱砂色
 export const vermilionTheme = {
   // 菜单栏 颜色
   "--menu-bg": "#fff",
   "--logo-bg": "#e9692b",
   "--logo-text-color": "#fff",
   "--menu-actvie-bg": "#fff",
   "--menu-hover-bg": "#fff",
   "--menu-text-color": "#333",
   "--menu-active-text-color": "#e9692b",
   "--menu-hover-text-color": "#e9692b",
   
   // 顶部导航栏 颜色
   "--header-bg": "#fff",
   "--header-text-color": "#333",
   "--header-hover-text-color": "#e9692b",
   "--primary-text-color": "#e9692b",
   
   // 内容区域配色
 ​
   "--border-color": "#dcdfe6",
 ​
   ...baseSize
 }

项目中使用

App.vue

 <template>
   <router-view v-slot="{ Component }">
     <keep-alive>
       <a-config-provider prefix-cls="custom">
         <component :is="Component"></component>
       </a-config-provider>
     </keep-alive>
   </router-view>
 </template>
 ​
 <script setup lang="ts">
 // 用的仓库是 Pinia 也可以使用其他库比如 Vuex
 import { themeStore } from "./store/theme";
 // AntDVue 中修改主题的API 其他库也有类似的 根据自己项目中实际的库做变更
 import { ConfigProvider } from 'ant-design-vue';
 // 上边定义的 initTheme 方法
 import { initTheme } from "@/theme";
 ​
 // 主题仓库
 const store = themeStore();
 // 修改Antd主题
 const initThemeConfig = (color: string) => {
   ConfigProvider.config({
     prefixCls: 'custom',
     theme: {
       // 成功相关主题颜色
       primaryColor: color,
       errorColor: "#FF4D4F",
       warningColor: "#FAAD14",
       successColor: "#52C41A",
       infoColor: "#57595C",
     },
   });
 }
 // 初始化调用一次
 initThemeConfig(store.getPrimary);
 // 初始化自定义主题 全局样式变量切换
 initTheme(store.getThemeType, store.getPrimary);
 ​
 // 监听到样式发生改变,切换主题
 store.$subscribe((mutations, state) => {
   initThemeConfig(state.primaryColor);
   initTheme(state.themeType, state.primaryColor)
 })
 </script>
 ​
 <style></style>

src/store/theme.ts

 import { defineStore } from "pinia";
 interface themeInter {
   primaryColor: string,
   themeType: string
 }
 export const themeStore = defineStore("theme", {
   state: ():themeInter => {
     return {
       // 主色系
       primaryColor: "#1890ff",
       // 主题色 Key 对应 src/theme/variable.ts 中声明的变量
       themeType: "blue"
     };
   },
   getters: {
     getPrimary(): string {
       return this.primaryColor;
     },
     getThemeType(): string {
       return this.themeType;
     }
   },
   // 同步和异步都可以写
   actions: {
     setPrimary(color: string) {
       this.primaryColor = color;
     },
     setThemeType(type: string) {
       this.themeType = type;
     }
   },
 })

到这里你已经成功掌握了一键换肤

源码地址

gitee.com/KeLuKeYa/vu…

本系列持续更新,后续功能会持续更新到次仓库

如果本篇文章对你有帮助 请替我点个小红心