vue + element ui根据域名换肤方案

573 阅读3分钟

vue + element ui根据域名换肤方案

背景

需求

项目本身是一套黄色的皮肤方案,新一期的需求重新部署了一套域名,新域名下的网站也需要做名称、图片、主题颜色的替换。前端皮肤主题也从原来的黄色需要更改为红色,区别于社区其他文章定制化element ui主题或者是点击按钮实现明/暗主题的切换,该需求需要根据域名切换定制化主题,且网站主色调也可以适配定制化主题(自定义色值部分)。

目标

  1. 根据域名切换主题皮肤
  2. 业务组件开发时主题色适配成本最小
  3. 兼容IE

实现方案

核心思路

  1. 确定主题切换判断条件,实现切换条件的域名判断函数
  2. 一个主题使用一个scss文件维护,根据判断条件,动态加载不同的element UI样式文件
  3. 根据判断条件,在body标签下挂载自定义属性data-thme
  4. 识别data-thme属性值,更改css var变量的值
  5. 业务代码组件主题色部分,采用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变量,项目中使用了postcsspostcss-custom-properties做了兜底,当使用IE时,会使用默认的主题颜色。

postcss-custom-properties实际上是将不支持var变量的地方用默认的颜色重写了一遍。

var样式报错.png

如果想进一步精确颜色,规避这个问题,则可以使用scss的@mixin@include的方案,不过这种写法有一定的繁琐性,需要特定的某一css属性上,不如使用同一个变量来得实在。