前言
随着现在的前端技术发展,对于UI界面的要求也是越来越高, 在实现框架搭建的时候, 主题切换就是我们考虑的点,今天就和大家讲一讲目前实现主题切换时的方案,以及分析一下各个方案的优劣势,附一个我实现好的主题切换demo地址 和 实现源码, 感兴趣的可以去看看。
分析实现思路
首先我们来分析一下实现的思路,主题切换其实说白了,就是使用不同的css代码来对应当前主题的颜色,举个简单的例子,现在页面上内容只有文字,我们现在要实现一个简单的亮色主题和暗色主题,那么我们只需要给不同主题场景下的的background-color
和color
不同的颜色就好了, 像这样:
css
#app.light {
background-color: #fff;
color: #000;
}
#app.dark {
background-color: #000;
color: #fff;
}
接着使用一个按钮去控制,切换主题的时候给对应的class
即可,很轻松就实现了主题的切换,但是这只是针对于简单场景,在复杂场景下,我们的业务代码是很多的,这样实现的代码非常的繁琐,且如果后续还有别的需求,如果新增一个其他主题,那么我们又不得不再去重新去实现一遍所有主题相关的css
代码,但是我们可以借着这个思路去优化我们的实现代码
使用var函数来实现主题切换
简单介绍一下 CSS 变量(自定义属性)
的使用机制和兼容性。
CSS 变量的使用:
-
定义变量:
- 使用
--variable-name
的格式来定义 CSS 变量。 - 变量可以定义在任何 CSS 选择器中,包括
:root
伪类。
css:root { --primary-color: #333; --secondary-color: #666; }
- 使用
-
使用变量:
- 使用
var(--variable-name, fallback-value?)
函数来引用 CSS 变量。 - 如果变量未定义,可以提供后备值作为降级方案。
css.my-component { background-color: var(--component-background, #fff); color: var(--primary-color); }
- 使用
-
变量的继承和级联:
- CSS 变量具有继承性和级联性,就像普通 CSS 属性一样。
- 子元素可以继承父元素定义的变量值。
- 同一个元素上定义的变量,后定义的会覆盖先前的定义。
css.parent { --color: red; } .child { color: var(--color); /* 继承父元素的 --color */ } .child { --color: blue; /* 覆盖父元素的 --color */ }
CSS 变量的兼容性:
-
现代浏览器支持:
- 现代浏览器(Chrome、Firefox、Safari、Edge)均支持 CSS 变量。
-
IE 浏览器兼容性:
- IE 11 及以下版本完全不支持 CSS 变量。
-
降级方案:
- 对于不支持 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地址查看,希望本文对大家有帮助,也希望大家多多点赞, 如果对我的文章感兴趣,也可以关注我便以获取我的后续更新。