(一) 前言
主题切换,是目前很多产品需要定制的功能,常见的比如需要给用户提供两套主题,日间和夜间模式,那么我们需要找出一种在实际项目中使用的方案
(二) 切换方案
已知道的主题切换方案有如下几种
将不同主题的样式抽取出来。
生成多份不同的主题样式文件。
动态引入。
比如
// theme1.css
.demo {
color: red;
}
// theme2.css
.demo {
color: green;
}
// theme3.css
.demo {
color: blue;
}
然后将3个css作为不同的css文件上传到服务器,在用户点击切换时候,link不同的css文件样式,但是弊端也很明显,需要同时维护多份主题文件,而且随着主题的扩展,将会增大维护成本
实现方式:
先把默认主题文件中涉及到颜色的 CSS 值替换成关键词:https://github.com/ElementUI/theme-preview/blob/master/src/app.vue#L250-L274
根据用户选择的主题色生成一系列对应的颜色值:https://github.com/ElementUI/theme-preview/blob/master/src/utils/formula.json
把关键词再换回刚刚生成的相应的颜色值:https://github.com/ElementUI/theme-preview/blob/master/src/utils/color.js
直接在页面上加 style 标签,把生成的样式填进去:https://github.com/ElementUI/theme-preview/blob/master/src/app.vue#L198-L211
饿了么这种实现方式严格来说只能算换肤,因为只是改变了皮肤颜色。而且全局替换颜色变量是比较暴力的一种方式。对于大型项目不建议采用。
我需要的主题切换方式是不同主题的样式代码打包在一起,通过应用不同主题的class来实现主题切换。类似下面的样子
.theme1 .demo {
...
}
.theme2 .demo {
...
}
.theme3 .demo {
...
}
这里我们通过sass的mixin来实现这样的主题映射。
(三) 具体实现
我们以vue为例子,实现一个主题切换方案
// store/types/global.js
export const GET_THEME = 'APP/GET_THEME';
export const UPDATE_THEME = 'APP/UPDATE_THEME';
// store/index.js
const initState = {
// ...
theme: 'light', // light / dark
};
const actions = {};
const getters = {
// ...
[globalTypes.GET_THEME](state) {
return state.theme;
},
};
const mutations = {
// ...
[globalTypes.UPDATE_THEME](state, theme) {
state.theme = theme;
},
};
const store = new Vuex.Store({
state: initState,
actions,
getters,
mutations,
modules: {
// ...
},
});
{{theme}}
上面步骤完成后,我们已经可以使用按钮切换vuex里面对应的主题字段,然后给最外层的class增加对应的主题name字段,当我们点击时候,就可以看到效果
// themes/common/variables.scss 配置主题变量
// theme
$themes: (
light: (
// color variables
primary-color: #f44,
// header
header-bg-color: #191a25,
header-text-color: $white-color,
),
dark: (
// color variables
primary-color: #2977b5,
// header
header-bg-color: #e4393c,
header-text-color: $black-color,
),
);
// themes/common/mixins.scss 主题映射方法
@mixin themify($themes: $themes) {
@each $theme-name, $map in $themes {
.theme-#{$theme-name} & {
$theme-map:() !global;
@each $key, $value in $map {
$theme-map: map-merge($theme-map, ($key: $value)) !global;
}
@content;
$theme-map: null !global;
}
}
}
@function themed($key) {
@return map-get($theme-map, $key);
}
这里有三点需要注意:
我们给theme-text 增加对应的样式
然后点击按钮,我们可以看到文本的主题,在我们点击按钮的时候,已经产生变化了
// light css样式
.theme-light .section-view .theme-text[data-v-a83bd3b0] {
color: #f44;
}
// dark 样式
.theme-dark .section-view .theme-text[data-v-a83bd3b0] {
color: #2977b5;
}
这样我们就将不同的样式打包成一个文件
这里我们要使用require进行动态导入,如果不理解import和require区别的请自行去查阅资料,
{{theme}}
当我们再次点击切换时候,已经可以根据具体的项目,切换到对应的静态资源了。