前端换肤方案 - element+less无感换肤(无需页面刷新)

前端换肤方案 - element+less无感换肤(无需页面刷新)

前言

前不久在改造一个迭代了一年多的项目时,增加了一个换肤功能。通过自己的探索,总结出了一套比较合适的改造方案供大家参考,如有更好的方案欢迎评论区踊跃评论😄

先上效果:

聊聊现有方案

在查阅现有方案时,总结了目前使用的几种方案:

1、定义多套样式

首先定义一套或多套样式变量,包括浅色和深色两种主题。在scss或less中使用变量,通过js改变root节点的class或属性来达到样式覆盖。 这种方式实现的前端换肤方案,可能会导致样式不易管理,查找样式复杂,每一套皮肤需要写一个css文件,造成多个css代码冗余。

css 复制代码
$dark-fill-1: #222;  
$dark-color-text: #fff;   
$dark-color-text-1: rgba(255,255,255,0.3);   
$dark-color-text-2: $color-brand1;

[data-theme="dark"] {   
  body { background: $dark-fill-1; }   
  .item .name { color: $dark-color-text; }   
  .item .desc { color: $dark-color-text-1; }   
  .header .text { color: $dark-color-text-2; }   
}

2、使用less传参

通过less或scss的传参属性,同样的只要改变根节点class或属性即可以改变页面样式,与第一点的优点是不需要写多份css文件,缺点是通过方法传入,当样式过多时,参数过多,需要改变某一个颜色成本高,容易造成问题。

less 复制代码
.theme(
    @mainPageBG: #f4f6ff,
    @fColor: #1b1e29,
    @vanBgColor: rgb(198, 183, 140),
    @vanColor: #fff
) {
    .home {
        background: @mainPageBG;
        color: @fColor;
    }
}

.themeWhite {
    .theme(#fff, #1b1e29, rgb(198, 183, 140), #fff);
}
.themeBlack {
    .theme(#090c14, #fff, rgb(198, 183, 140), #fff);
}

3、VueUse+css变量快速切换

使用外部库VueUse的useDark快速切换黑暗和明亮模式。

xml 复制代码
<script setup lang="ts">
import { useToggle } from '@vueuse/shared'
import { isDark } from '../../.vitepress/theme/composables/dark'

// const isDark = useDark()
const toggleDark = useToggle(isDark)
</script>

<template>
  <button @click="toggleDark()">
    <i inline-block align-middle i="dark:carbon-moon carbon-sun" />

    <span class="ml-2">{{ isDark ? 'Dark' : 'Light' }}</span>
  </button>
</template>

最终解决方案

接触了解了上述几个方案后,结合项目本身架构(less+element+echarts+map),最终采用了以下使用方案。

less变量处理

1、定义不同肤色的css变量

css 复制代码
.themeDark {
    --theme-color: #141414;
    --header-primary-color: #1a3750;
    --bg-content-default: #1a3750;
    --bg-theme-default: #13293b;
    --bg-left-color: #27415a;
}

.themeLight {
    --theme-color: #f4f4f4;
    --header-primary-color: #2670ff;
    --bg-content-default: #fff;
    --bg-theme-default: #f3f5f9;
    --bg-left-color: #eff3f4;
}

2、引入css变量,并通过定义less变量使用,解决了通过方法传参,参数过多的问题。

less 复制代码
//换肤相关
@import './dark.less';
@import './light.less';

@theme-color: var(--theme-color-te);
@header-primary-color: var(--header-primary-color);
@bg-content-default: var(--bg-content-default);
@bg-left-color: var(--bg-left-color);
@bg-theme-default: var(--bg-theme-default);
@font-default-color: var(--font-default-color);

element 主题切换

1、可以在element官网定制和下载不同主题的style文件,将css文件更名为less,在最外层增加自定义的类。

arduino 复制代码
.themeDark{
    //下载的css文件
    ...
}

2、为了开发中不用每次去编译这个较大的less文件,再通过less命令,将less转为css

复制代码
lessc + 空格 + less文件名

最终效果:

css 复制代码
@charset "UTF-8";
.themeDark .fade-in-linear-enter-active,
.themeDark .fade-in-linear-leave-active {
  -webkit-transition: opacity 0.2s linear;
  transition: opacity 0.2s linear;
}
...

3、main.js中引入

echarts

因为项目中使用了大量的echarts,在动态切换肤色时echarts需要重新加载才能改变,因此,需要通过监听全局变量,当改变皮肤时重新渲染echarts。将当前主题存放到vuex中

kotlin 复制代码
watch:{
        '$store.state.myTheme'() {
            this.getAllData();
        }
}

map

项目中使用到了腾讯地图,所以也需要更改在不同主题下不同的地图主题。在使用地图时我们知道,在引入地图时需要去引入地图的入口文件,入口文件中包含着需要加载的地图资源,所以我们只需要在腾讯地图官网定义不同的主题颜色,将其style.json保留下来,修改入口文件中的配置。

ini 复制代码
...
styleSrc: theme == 'themeDark' ? '/static/style.json' : 'https://xxxx/static/cdn/style{id}/style.json',
style3DSrc:  theme == 'themeDark'  ? '/static/style.json' : 'https://xxxx/static/cdn/style{id}/style.json',
...

js改变主题

做好以上准备工作,在main.js中在改变html的上的属性以及vuex来实现不用刷新更换不同主题。

将需要设置的主题存放到Storage中,以保证刷新后主题颜色不变,为了兼容UI框架(比如dialog会将dom渲染到body外),所以将class置于最顶层html上,改变主题的时候改变html上的class,这样下层所有的less变量将会使用该class下的颜色主题。修改vuex的值,用于触发echarts的刷新。

typescript 复制代码
/**
* 切换肤色
*/
changeColor(type) {
    this.themeClass = type;
    window.sessionStorage.setItem('themeClass', this.themeClass);
    //动态修改html class,为了兼容UI框架,将class置于最顶层
    const themeArr = ['themeDark', 'themeLight'];
    let tempArr = document.querySelector('html').classList;
    tempArr.forEach((item) => {
        if (themeArr.includes(item)) {
            document.querySelector('html').classList.remove(item);
        }
    });
    document.querySelector('html').classList.add(this.themeClass);

    //修改echarts的颜色
    if (type == 'themeLight') {
        Vue.prototype.$themeChartColor = '#333';
    } else {
        Vue.prototype.$themeChartColor = '#fff';
    }
    //修改store的值,可用于触发相操作
    this.$store.commit('setMyTheme', type);
},

结语

以上便是本次换肤分享,由于该项目刚开发时并没有考虑到后续需要换肤的方案,在本次开发时也遇到很多细节问题,比如之前开发时主题色、各种状态颜色标准使用不一致,改造时我尽量去将一些相近的颜色做到统一,保持系统整体一致性。本次迭代也用了较短的时间实现了这个换肤功能,总体效率上来说这种方案和体验是比较好的。

相关推荐
崔庆才丨静觅2 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60613 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了3 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅3 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅3 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅4 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment4 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅4 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊4 小时前
jwt介绍
前端
爱敲代码的小鱼4 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax