一、背景
在最近的一个需求中,一个商业产品要在不同APP中需要使用不同的CSS主题,这就涉及到了动态切换CSS主题的方案
二、方案简介
sass+mixin+js设置html的data-theme属性
原理是使用sass配合mixin编程成固定的css,通过js设定不同的主题,使对应的css生效
1. 定义变量
使用sass的map,可以把所涉及到的变量通过配置的方式整理起来,其中第一层是主题名,第二层是css的value,例如bg、info-price
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | @use "sass:map";
 $themes: (
 appA: (
 bg: url('https://wos.58cdn.com.cn/cDazYxWcDHJ/picasso/2m9p9qlv__w1500_h520.png') no-repeat 0 0px / 100%,
 info-price: #FF552E,
 ),
 appB: (
 bg: url('https://wos.58cdn.com.cn/cDazYxWcDHJ/picasso/muc3req9__w1500_h520.png') no-repeat 0 0px / 100%,
 info-price: #09D57E,
 ),
 appC: (
 bg: url('https://wos.58cdn.com.cn/cDazYxWcDHJ/picasso/7re7pg8r__w1500_h520.png') no-repeat 0 0px / 100%,
 info-price: #FF704F,
 ),
 );
 
 | 
2. mixin方法的实现
通过mixin实现把css根据不同主题注入页面
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
 | @use "sass:map";
 // 遍历主题数据,把@content中的css注入页面中
 @mixin injectCss {
 @each $theme-name, $theme-map in $themes {
 //!global 把局部变量强升为全局变量,让下方的setCss方法也可以使用$theme-map
 $theme-map: $theme-map !global;
 //判断html的data-theme的属性值  #{}是sass的插值表达式
 //& sass嵌套里的父容器标识   @content是混合器插槽,像vue的slot
 [data-theme="#{$theme-name}"] & {
 @content;
 }
 }
 }
 
 // 注入css
 @mixin setCss($css, $variable) {
 @include injectCss {
 #{$css}: map-get($theme-map, $variable);
 }
 }
 
 | 
3. 通过js设置主题
| 12
 3
 4
 5
 6
 7
 
 | const app = utils.getApp();const themeMap = {
 appA: 'appA',
 appB: 'appB',
 appC: 'appC',
 };
 window.document.documentElement.setAttribute('data-theme', themeMap[app] || 'appA');
 
 | 
4. 在页面中使用mixin
| 12
 3
 4
 5
 
 | @import '@css/m/mini-theme.scss';
 .buy-container {
 @include setCss("background", "bg");
 }
 
 |