LESS/SCSS 高效主题换肤方案

发布于:2025-07-06 ⋅ 阅读:(14) ⋅ 点赞:(0)

LESS/SCSS 高效主题换肤方案

设计思路

通过预处理器的变量映射和混入功能,我们可以实现一套高效、无感的主题换肤系统,核心思路如下:

  1. 定义主题变量映射表:使用 LESS 的映射或 SCSS 的 map 存储不同主题的变量值
  2. 创建混入方法:封装主题变量获取逻辑
  3. 动态切换主题类名:通过修改顶层类名实现主题切换
  4. 自动编译多主题CSS:利用预处理器生成多套主题样式

LESS 实现方案

1. 定义主题变量映射

// 定义主题映射
@themes: {
  light: {
    primary-color: #1890ff;
    bg-color: #ffffff;
    text-color: #333333;
  };
  dark: {
    primary-color: #177ddc;
    bg-color: #141414;
    text-color: #f0f0f0;
  };
};

2. 创建主题混入方法

// 主题变量获取混入
.theme-mixin(@property, @key) {
  @{property}: .get-theme-value(@key)[];
}

// 辅助函数获取主题值
.get-theme-value(@key) {
  each(@themes, {
    @theme-name: @key;
    @theme-vars: @value;
    
    .@{theme-name} & {
      @theme-value: @theme-vars[@@key];
    }
  });
  
  @return: @theme-value;
}

3. 使用混入定义样式

// 应用主题混入
.container {
  .theme-mixin(background-color, bg-color);
  .theme-mixin(color, text-color);
  padding: 20px;
}

.button {
  .theme-mixin(background-color, primary-color);
  .theme-mixin(color, text-color);
  border: none;
  padding: 8px 16px;
}

4. 编译输出

最终会生成类似这样的CSS:

.light .container {
  background-color: #ffffff;
  color: #333333;
}
.dark .container {
  background-color: #141414;
  color: #f0f0f0;
}

/* 按钮样式同理 */

SCSS 实现方案

1. 定义主题变量映射

// 定义主题映射
$themes: (
  light: (
    primary-color: #1890ff,
    bg-color: #ffffff,
    text-color: #333333,
  ),
  dark: (
    primary-color: #177ddc,
    bg-color: #141414,
    text-color: #f0f0f0,
  )
);

2. 创建主题混入方法

// 主题混入
@mixin theme($property, $key) {
  @each $theme-name, $theme-vars in $themes {
    .#{$theme-name} & {
      #{$property}: map-get($theme-vars, $key);
    }
  }
}

3. 使用混入定义样式

// 应用主题混入
.container {
  @include theme(background-color, bg-color);
  @include theme(color, text-color);
  padding: 20px;
}

.button {
  @include theme(background-color, primary-color);
  @include theme(color, text-color);
  border: none;
  padding: 8px 16px;
}

高级优化方案

1. 主题变量收敛

将主题相关的所有变量收敛到一个混入中:

// LESS 版本
.theme-variables() {
  each(@themes, {
    @theme-name: @key;
    @theme-vars: @value;
    
    .@{theme-name} & {
      @primary-color: @theme-vars[primary-color];
      @bg-color: @theme-vars[bg-color];
      @text-color: @theme-vars[text-color];
    }
  });
}

// 使用
.container {
  .theme-variables();
  background-color: @bg-color;
  color: @text-color;
}

2. 按需加载主题CSS

通过构建工具分割CSS,实现按需加载:

// webpack.config.js
const themes = ['light', 'dark'];

module.exports = themes.map(theme => ({
  entry: './src/styles/index.less',
  output: {
    filename: `${theme}.css`
  },
  module: {
    rules: [{
      test: /\.less$/,
      use: [
        MiniCssExtractPlugin.loader,
        'css-loader',
        {
          loader: 'less-loader',
          options: {
            lessOptions: {
              modifyVars: {
                'current-theme': theme
              }
            }
          }
        }
      ]
    }]
  }
}));

3. JavaScript 主题切换

// 切换主题
function switchTheme(themeName) {
  document.documentElement.className = themeName;
  localStorage.setItem('theme', themeName);
}

// 初始化主题
const savedTheme = localStorage.getItem('theme') || 'light';
switchTheme(savedTheme);

性能优化建议

  1. 减少主题变量数量:只对必要的样式应用主题
  2. 使用CSS变量作为降级方案:现代浏览器可使用原生CSS变量
  3. 服务端渲染处理:在SSR时注入正确的主题类名
  4. 主题样式合并:将主题样式集中管理,避免分散定义

这套方案通过预处理器的强大功能,实现了开发时的简洁高效和运行时的无感切换,同时保持了良好的可维护性和扩展性。