前端主题换肤技术总结
由于平常总是遇到各类换肤的需求,在此做下大概总结,以便对各种情况的换肤场景进行参考。
换肤场景
换肤常见的场景有以下几种:
- 黑夜/白天模式切换
- 主题换肤,如QQ邮箱的主题皮肤
- 客制化配色,如同一应用针对不同渠道定制的不同配色
其中黑夜/白天模式切换与主题换肤之间的概念类似,只是交互逻辑上略有区别。
换肤需求
换肤根据需求来讲可以分为:
- 静态换肤,提供几种可选择的颜色/主题样式,进行选择切换,一般可供选择的主题样式不会太多
- 动态换肤,可自定义色值,可通过取色板取色或者后端接口下发,可选择的范围比较大
思路
讲完了换肤类型,接下来分析几种常规的换肤手段
1.Less 变量/ Sass 变量
可以说是是最常见的换肤手段,利用 Less 变量对应用样式进行重新定义,如:
// variable-theme.less
@color-primary: #E3454B;
@color-up: #E53939;
@color-down: #43A047;
@color-flat: #545A71;
@color-buy: #E63232;
@color-sell: #2E83F2;
然后在对应属性下使用:
// button.less
.button-primary {
background-color: @color-primary;
}
但是这时候我们发现,Less 变量只能影响 CSS,不能影响 JS,这时候如果遇到需求需要配置如图表样式、JS 生成的 style 样式等 JS 控制的样式时,Less 变量就变得力不从心了。查阅了一系列资料得知,CSS Modules 中有一个简洁的实用指令,称为 :export,:export 指令的工作原理基本上类似于 ES6 的 export 关键字。在 webpack 的帮助下,:export 将导出一个对象,其中包含要在 JS 中使用的变量名称及其关联值,这些值都会作为字符串导出。 export.less:
@import "./variable-theme.less";
:export {
colorPrimary: @color-primary;
colorUp: @color-up;
colorDown: @color-down;
colorFlat: @color-flat;
colorBuy: @color-buy;
colorSell: @color-sell;
}
index.js
import variablesLess from './export.less';
// 导出变量
export default variablesLess;
注意,如果要使用 :export,且当前 css-loader 版本 >= 4,需要将其配置项 "modules.mode" 修改为 "icss"。
以上步骤,我们已经完成了对项目的绝大多数内容的配置,这时候需要的就是通过各种方式改变 variable-theme.less
** **文件中的 Less 变量即可,常见的方式是通过 webpack 改变引用的变量文件,如 variable-theme.less
,来进行变量替换。或者预先引用不同主题变量文件,打出不同的css文件,在运行时通过读取 url 参数等方式进行引用替换:
以上只是两种常见的实现方式,这里不针对展开赘述,可根据实际场景自行选择方案。
2.CSS变量+css-vars-ponyfill
先来说下 CSS自定义变量 ,它让我拥有像 Less/Sass 那种定义变量并使用变量的能力,声明变量的时候,变量名前面要加两根连词线( --),在使用的时候只需要使用 var() 来访问即可,看下效果: 使用 CSS自定义变量 的好处就是我们可以使用 JS 来改变这个变量:
- 使用
document.body.style.setProperty('--bg' ,'#ff0000' );
来设置变量 - 使用
document.body.style.getPropertyValue('--bg' );
来获取变量 - 使用
document.body.style.removeProperty('--bg' );
来删除变量
有了如上的准备,我们基于 css变量 来实现的换肤就有思路了:将 CSS 中与换肤有关的颜色值提取出来放在 :root{} 中,然后在页面上使用 setProperty 来动态改变这些变量值即可。
然而 CSS自定义变量 有个致命的缺点,就是不支持IE和部分低版本的移动端浏览器内核,这个时候就需要使用到 css-vars-ponyfill
这个polyfill来搞定IE了,具体使用方法可参考 换肤解决方案1-css变量法
3.CSS优先级覆盖法
这也是所有方法中最不推荐的一种方法,之所以写出来,是因为前两种方案都是基于改造项目整体的情况下进行的,而优先级覆盖法作为入侵最少的方案,也确实在特殊情况下有一定的用武之地,一句话概括就是创建一份主题包文件,通过对其编写优先级较高的 CSS样式 来覆盖原项目的样式,并使用类似引用不同主题变量文件的方式来进行主题切换。当然,入侵少便意味着耦合高,原样式的些许更改就可能对主题包有破坏性的影响,因此在使用这种方法的时候需要慎重考虑。
总结
除了以上的这些方法外,其实还有很多解决方案,包括针对换肤过程中的触发场景、替换方案、持久化场景等都有各种解法,往后有时间也会慢慢补充上来自己的一些积累。
转载自:https://juejin.cn/post/7203364379340865596