一文说透前端主题切换的思路和实现

前言

随着现在的前端技术发展,对于UI界面的要求也是越来越高, 在实现框架搭建的时候, 主题切换就是我们考虑的点,今天就和大家讲一讲目前实现主题切换时的方案,以及分析一下各个方案的优劣势,附一个我实现好的主题切换demo地址实现源码, 感兴趣的可以去看看。

分析实现思路

首先我们来分析一下实现的思路,主题切换其实说白了,就是使用不同的css代码来对应当前主题的颜色,举个简单的例子,现在页面上内容只有文字,我们现在要实现一个简单的亮色主题和暗色主题,那么我们只需要给不同主题场景下的的background-colorcolor 不同的颜色就好了, 像这样:

css 复制代码
#app.light {
  background-color: #fff;
  color: #000;
}
#app.dark {
  background-color: #000;
  color: #fff;
}

接着使用一个按钮去控制,切换主题的时候给对应的class即可,很轻松就实现了主题的切换,但是这只是针对于简单场景,在复杂场景下,我们的业务代码是很多的,这样实现的代码非常的繁琐,且如果后续还有别的需求,如果新增一个其他主题,那么我们又不得不再去重新去实现一遍所有主题相关的css代码,但是我们可以借着这个思路去优化我们的实现代码

使用var函数来实现主题切换

简单介绍一下 CSS 变量(自定义属性)的使用机制和兼容性。

CSS 变量的使用:

  1. 定义变量:

    • 使用 --variable-name 的格式来定义 CSS 变量。
    • 变量可以定义在任何 CSS 选择器中,包括 :root 伪类。
    css 复制代码
    :root {
      --primary-color: #333;
      --secondary-color: #666;
    }
  2. 使用变量:

    • 使用 var(--variable-name, fallback-value?) 函数来引用 CSS 变量。
    • 如果变量未定义,可以提供后备值作为降级方案。
    css 复制代码
    .my-component {
      background-color: var(--component-background, #fff);
      color: var(--primary-color);
    }
  3. 变量的继承和级联:

    • CSS 变量具有继承性和级联性,就像普通 CSS 属性一样。
    • 子元素可以继承父元素定义的变量值。
    • 同一个元素上定义的变量,后定义的会覆盖先前的定义。
    css 复制代码
    .parent {
      --color: red;
    }
    
    .child {
      color: var(--color); /* 继承父元素的 --color */
    }
    
    .child {
      --color: blue; /* 覆盖父元素的 --color */
    }

CSS 变量的兼容性:

  1. 现代浏览器支持:

    • 现代浏览器(Chrome、Firefox、Safari、Edge)均支持 CSS 变量。
  2. IE 浏览器兼容性:

    • IE 11 及以下版本完全不支持 CSS 变量。
  3. 降级方案:

    • 对于不支持 CSS 变量的浏览器,可以使用 Autoprefixer 等工具生成回退的 CSS 代码。
    • 在 CSS 中使用 var() 函数时,可以提供后备值作为降级方案。
    css 复制代码
    .my-component {
      background-color: #fff; /* 回退样式 */
      background-color: var(--component-background, #fff);
    }

了解完css 变量的特性,那么实现起来就很简单了,我们需要一个 theme.css 的页面用来专门存储我们的主题变量,将变量直接给到html根节点上,其子节点的内容就可以通过继承来使用,我们通过切换根节点的class来对应不同的变量,这里需要注意, css变量最好给一个前缀用来标识,避免被子节点的css变量覆盖导致出现问题,在多人协作开发更是需要注意这点,那么我们的代码就应该是这样:

css 复制代码
:root {
  --base-color: #212122;
  --bg-color: #f8f8f8;
  // 其他主题相关变量
}

html.dark {
  --base-color: #f8f8f8;
  --bg-color: #212122;
  // 其他主题相关变量
}

在定义好之后,我们就可以使用定义的变量来使用了,在其他使用的地方

css 复制代码
#app {
  background-color: var(--bg-color);
  color: var(--base-color);
}

这样,我们就轻松使用 css变量 实现了主题的切换,但是问题来了,这样实现的主题切换会有兼容性问题,虽然我们可以使用降级处理来避免出现bug, 但是这样我们的主题切换功能也会受到影响,如果在考虑浏览器兼容的情况下,我们就不得不使用别的方案来实现我们的主题切换

通过css预编译处理器来实现

前端有很多的预编译处理器, 预编译处理器是一种可以扩展 CSS 语法并在构建时编译为标准 CSS 的工具,由于预编译处理器通过打包之后会变成兼容的css代码,所以我们可以使用这种方式来实现主题切换。 以 scss 为例, 我们定义一个 scss 变量来存储主题的值, 通过循环,让scss打包出多套css 代码,从而实现主题切换, 如下例:

ruby 复制代码
$themes: (
  light: (
    bgColor: #f8f8f8,
    textColor: #212122
  ),
  dark: (
    bgColor: #212122,
    textColor: #f8f8f8
  )
);

$currentTheme: light;
@mixin useTheme() {
  @each $key, $value in $themes {
    $currentTheme: $key !global;
    html[class='#{$key}'] & {
      @content
    }
  }
}

@function getVar($key) {
  $themeMap: map-get($map: $themes, $key: $currentTheme);
  @return map-get($map: $themeMap, $key: $key)
}

这样就定义好了一个打包成不同代码的函数和mixin, 在需要针对不同主题做对应处理的地方,我们就可以这样实现:

less 复制代码
@import "@/theme.scss";

#app {
  @include useTheme {
    background-color: getVar('bgColor');
    color: getVar('textColor');
  }
}

这里说个小 tips 因为我们需要处理的地方很多, 每次都通过 @import 来引入文件其实很麻烦,可以通过脚手架配置去自动全局引入,webpack和vite等主流工具都支持,这里不详细展开了,以vite简单举例,只需要如下配置:

css 复制代码
export default {
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: '@import "@/theme.scss";'
      }
    }
  }
}

这样就能不需要每次导入之后使用了。

方案优劣势对比

CSS 变量 (var) 实现主题切换:

优势:

  • 灵活性强: 可以在任何 CSS 规则中定义和使用变量,非常灵活。
  • 无需构建时编译: 浏览器原生支持 CSS 变量,无需预编译即可使用。
  • 主题切换简单: 只需要动态修改 HTML 元素的 class 即可切换主题,非常方便。
  • 代码简洁: 使用 CSS 变量实现主题切换的代码相对更加简洁。

缺点:

  • 兼容性较差: IE 11 及以下版本完全不支持 CSS 变量,需要特殊处理。
  • 无法利用 SCSS 的其他功能: 无法使用 SCSS 的嵌套、混合器等高级特性。

SCSS 实现主题切换:

优势:

  • 兼容性更好: SCSS 编译后的 CSS 代码可以很好地兼容 IE 11 及以下浏览器。
  • 功能强大: SCSS 提供了丰富的语法特性,如嵌套、混合器、函数等,可以更好地组织和管理样式代码。
  • 社区资源丰富: SCSS 有着广泛的社区支持和大量的可复用资源。

缺点:

  • 需要构建时编译: SCSS 代码需要经过编译才能转换为浏览器可识别的 CSS 代码,增加了构建过程的复杂性。
  • 主题切换稍显复杂: 使用 SCSS 实现主题切换需要更多的代码,并且需要在构建时生成多套 CSS 文件。

文末

至此,相信大家对前端的主流主题切换方案都有所了解,如果想观看详细的代码,可以去我开头附的demo地址查看,希望本文对大家有帮助,也希望大家多多点赞, 如果对我的文章感兴趣,也可以关注我便以获取我的后续更新。

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