Vue 在sass中的主题切换方案

(一) 前言

主题切换,是目前很多产品需要定制的功能,常见的比如需要给用户提供两套主题,日间和夜间模式,那么我们需要找出一种在实际项目中使用的方案

(二) 切换方案

已知道的主题切换方案有如下几种

  1. DWZ富客户端
    实现方式:

将不同主题的样式抽取出来。
生成多份不同的主题样式文件。
动态引入。

比如

//  theme1.css
.demo {
  color: red;
}

//  theme2.css
.demo {
  color: green;
}

//  theme3.css
.demo {
  color: blue;
}

然后将3个css作为不同的css文件上传到服务器,在用户点击切换时候,link不同的css文件样式,但是弊端也很明显,需要同时维护多份主题文件,而且随着主题的扩展,将会增大维护成本

  1. 饿了么换肤功能

实现方式:

先把默认主题文件中涉及到颜色的 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

饿了么这种实现方式严格来说只能算换肤,因为只是改变了皮肤颜色。而且全局替换颜色变量是比较暴力的一种方式。对于大型项目不建议采用。

  1. 打包不同主题的样式在一个css

我需要的主题切换方式是不同主题的样式代码打包在一起,通过应用不同主题的class来实现主题切换。类似下面的样子

.theme1 .demo {
  ...
}

.theme2 .demo {
  ...
}

.theme3 .demo {
  ...
}

这里我们通过sass的mixin来实现这样的主题映射。

(三) 具体实现

我们以vue为例子,实现一个主题切换方案

  1. 在vuex中定义一个主题字段并给其对应的mutations方法和getters方法
// 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: {
    // ...
  },
});
  1. 在入口的App.vue增加对应的代码






上面步骤完成后,我们已经可以使用按钮切换vuex里面对应的主题字段,然后给最外层的class增加对应的主题name字段,当我们点击时候,就可以看到效果

  1. 设置sass混合
    现在我们来给不同的主题提供选择的混合样式
// 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);
}

这里有三点需要注意:

  • 您可以在主题图中定义任何CSS属性,而不仅仅是颜色;
  • 你在@themify mixin中包装所有主题属性,并通过调用themed()具有属性名称的函数来访问在themes中定义的属性。在themed()外面使用@themify不起作用。
  • 这种方法的一个限制是你不能嵌套@themify mixin。因为保持代码化的代码段尽可能短,以保持最终的CSS尽可能小,这是一个好的限制。
  1. 使用sass定制css样式

我们给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;
}

这样我们就将不同的样式打包成一个文件

  1. 定义不同的静态资源

这里我们要使用require进行动态导入,如果不理解import和require区别的请自行去查阅资料,







当我们再次点击切换时候,已经可以根据具体的项目,切换到对应的静态资源了。

你可能感兴趣的:(Vue,CSS)