LESS/SCSS 高效主题换肤方案
设计思路
通过预处理器的变量映射和混入功能,我们可以实现一套高效、无感的主题换肤系统,核心思路如下:
- 定义主题变量映射表:使用 LESS 的映射或 SCSS 的 map 存储不同主题的变量值
- 创建混入方法:封装主题变量获取逻辑
- 动态切换主题类名:通过修改顶层类名实现主题切换
- 自动编译多主题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);
性能优化建议
- 减少主题变量数量:只对必要的样式应用主题
- 使用CSS变量作为降级方案:现代浏览器可使用原生CSS变量
- 服务端渲染处理:在SSR时注入正确的主题类名
- 主题样式合并:将主题样式集中管理,避免分散定义
这套方案通过预处理器的强大功能,实现了开发时的简洁高效和运行时的无感切换,同时保持了良好的可维护性和扩展性。