vue + element ui根据域名换肤方案
背景
需求
项目本身是一套黄色的皮肤方案,新一期的需求重新部署了一套域名,新域名下的网站也需要做名称、图片、主题颜色的替换。前端皮肤主题也从原来的黄色需要更改为红色,区别于社区其他文章定制化element ui主题或者是点击按钮实现明/暗主题的切换,该需求需要根据域名切换定制化主题,且网站主色调也可以适配定制化主题(自定义色值部分)。
目标
- 根据域名切换主题皮肤
- 业务组件开发时主题色适配成本最小
- 兼容IE
实现方案
核心思路
- 确定主题切换判断条件,实现切换条件的域名判断函数
- 一个主题使用一个scss文件维护,根据判断条件,动态加载不同的element UI样式文件
- 根据判断条件,在body标签下挂载自定义属性
data-thme - 识别
data-thme属性值,更改cssvar变量的值 - 业务代码组件主题色部分,采用css变量编写
目录简化结构
├── src
│ └── App.vue
│ └── main.js
│ └── style
│ └── components.scss
│ └── element-variables.scss
│ └── element-variables-red.scss
│ └── variables.scss
│ └── index.scss
├── vue.config.js
components.scss:自定义element组件样式,所有主题通用element-variables.scss:默认主题的element UI样式变量element-variables-red.scss:特定主题的element UI样式变量variables.scss:项目通用变量index.scss:样式入口文件
代码实现
element UI主题切换
element-variables.scss
$--color-primary: #ff8e36;
$--color-success: #25a762;
$--color-danger: #f04134;
$--color-warning: #f7912e;
$--color-text-primary: #333;
$--color-text-regular: #666;
$--color-text-secondary: #999;
$--color-text-placeholder: #ccc;
$--font-path: '~element-ui/lib/theme-chalk/fonts';
@import '~element-ui/packages/theme-chalk/src/index';
@import "./components.scss"; // 注意样式优先级,加载修改过后的组件样式
:export {
theme: $--color-primary;
}
main.js
// 根据域名更换elementUI主题
import './style/element-variables.scss' // 默认主题
import { judgeIsRedHost } from '@utils/hosts'
const isRedHost = judgeIsRedHost()
if (isRedHost) {
import('./style/element-variables-red.scss')
}
- 项目主入口文件,可以根据域名的不同,导入不同的element UI样式变量文件
css变量配置
variables.scss
$--theme-color-primary: var(--theme-color-primary, #ff8e36);
$--theme-color-success: var(--theme-color-success, #25a762);
$--theme-color-danger: var(--theme-color-danger, #f04134);
$--theme-color-warning: var(--theme-color-warning, #f7912e);
$--theme-color-text-primary: #333;
$--theme-color-text-regular: #666;
$--theme-color-text-secondary: #999;
$--theme-color-text-placeholder: #ccc;
[data-theme="red"] body {
--theme-color-primary: #C21B2F;
}
- data-theme是挂载在body节点下的自定义属性,是换肤的核心要点之一。通过
[data-theme="red"] body的样式选择,使得变量--theme-color-primary被赋予不同的值,故页面可以展示不同的颜色。
App.vue
<script>
export default {
created() {
const hostKey = getHostKey()
document.documentElement.setAttribute('data-theme', hostKey)
}
}
</script>
css变量挂载和使用
vue.config.js
chainWebpack(config) {
const oneOfsMap = config.module.rule('scss').oneOfs.store
oneOfsMap.forEach(item => {
item.use('sass-resources-loader')
.loader('sass-resources-loader')
.options({
resources: ['./src/style/variables.scss']
}).end()
})
}
- 由于所有的变量都是放在同一个文件当中,webpack也可以使用sass-resources-loader这样的插件,将变量注入到所有的vue文件当中,这样的话,项目中使用起来就比较方便了,某些特殊情况下可以直接用
[data-theme="red"]的形式对某一主题特定修改,例如:
.double-quotation-mark {
position: absolute;
font-size: 65px;
color: $--theme-color-primary; // 这里使用了变量
letter-spacing: 0;
}
// 选定某种主题下有针对性的修改样式
[data-theme="red"] & {
.double-quotation-mark {
color: #c21b2f;
}
}
IE兼容
由于IE不支持css var变量,项目中使用了postcss的postcss-custom-properties做了兜底,当使用IE时,会使用默认的主题颜色。
postcss-custom-properties实际上是将不支持var变量的地方用默认的颜色重写了一遍。
如果想进一步精确颜色,规避这个问题,则可以使用scss的@mixin和@include的方案,不过这种写法有一定的繁琐性,需要特定的某一css属性上,不如使用同一个变量来得实在。