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

前言

随着现在的前端技术发展,对于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 小时前
新手小白如何挖掘cnvd通用漏洞之存储xss漏洞(利用xss钓鱼)
运维·服务器·前端·网络·安全·web3·xss
正小安1 小时前
Vite系列课程 | 11. Vite 配置文件中 CSS 配置(Modules 模块化篇)
前端·vite
暴富的Tdy1 小时前
【CryptoJS库AES加密】
前端·javascript·vue.js
neeef_se1 小时前
Vue中使用a标签下载静态资源文件(比如excel、pdf等),纯前端操作
前端·vue.js·excel
m0_748235611 小时前
web 渗透学习指南——初学者防入狱篇
前端
z千鑫2 小时前
【前端】入门指南:Vue中使用Node.js进行数据库CRUD操作的详细步骤
前端·vue.js·node.js
m0_748250742 小时前
Web入门常用标签、属性、属性值
前端
m0_748230442 小时前
SSE(Server-Sent Events)返回n ,前端接收数据时被错误的截断【如何避免SSE消息中的换行符或回车符被解释为事件消息的结束】
前端