Vue+Less换肤方案

1. 背景

很多前端项目都有换肤的需求,笔者在此分享笔者当前所使用的换肤方案。本文将讲述Vue+Less前端项目换肤的设计思路以及开发皮肤主题的步骤。虽然笔者在本文中所使用的是Vue+Less,但是针对该方案而言,亦可扩展为:Vue/React/Angular+Less/Scss。读者在阅读本文时,请着重关注换肤的设计思路,不必局限于具体的技术。

2. 设计思路

此小节将描述主题换肤的设计思路。整体思路是以 mixins + variable 的形式实现换肤的功能。在具体的主题文件,只需定义主题中的变量以及执行 mixins 函数即可。

2.1 定义主题配置

// src/store/theme-map.js

export default {
  BLUE: {
    value: 'blue',
    name: '蓝色主题',
    color: '#2d8cf0',
  },
  ORANGE: {
    value: 'orange',
    name: '橙色主题',
    color: '#e96500',
  },
  RED: {
    value: 'red',
    name: '红色主题',
    color: '#e43e31',
  },
}

2.2 保存当前主题设置

  • 在 vuex 中存储当前主题
  • 在 localStorage 中存储当前主题
  • 调用后台 api 接口,将主题数据保存到数据库

2.2.1 Vuex 部分示例代码

import THEME_MAP from './theme-map'

export const THEME = 'APP_THEME'

if (!LocalStorageUtils.readJSON(THEME)) {
  LocalStorageUtils.writeJSON(THEME, THEME_MAP.BLUE)
}

export default new Vuex.Store({
  namespaced: true,
  state: {
    theme: LocalStorageUtils.readJSON(THEME),
  },
  mutations: {
    [THEME] (state, payload) {
      state.theme = payload
    },
  },
  actions: {},
  modules: {},
})

特别说明:

2.3 读取当前主题设置

  • 刷新页面时:从 localStorage 读取当前主题设置并同步到 vuex
  • 重新登录时:从登录接口返回的数据里读取用户主题设置并同步到 vuex、localStorage

2.4 编写主题 mixins 文件

.themeMixins() {
  .el-button--primary {
    background-color: @primary-color;
    border-color: @primary-color;
    color: @button-text-color;
  }

  .component.layout-menu .el-menu .is-active {
    background-color: @primary-color;
  }

  .el-tag {
    color: @primary-color;
  }
}

2.5 在样式入口文件引入主题 mixins 文件

@import './theme-mixins.less';

2.6 编写主题文件

// src/assets/styles/themes/red.less

示例:

body.red {
  @primary-color: #ff503e;
  @button-text-color: #fff;

  .themeMixins();
}

2.7 在样式入口文件引入主题文件

@import './theme-mixins.less';
@import './themes/red.less';

2.8 编写切换主题的业务组件

<template>
  <el-dropdown trigger="click" class="theme-switch">
    <span class="el-dropdown-link cursor">
      <i class="icon icon-tshirt" :style="{ color: theme.color }"></i>
    </span>
    <el-dropdown-menu slot="dropdown" class="theme-switch">
      <el-dropdown-item
        v-for="i in themeList"
        :key="i.value"
        @click.native="onThemeItemClick(i)"
      >
        <div class="theme-item" :class="[i.value]">
          <i class="circle" :style="{ backgroundColor: i.color }"></i>
          <span :style="{ color: i.color }">{{ i.name }}</span>
        </div>
      </el-dropdown-item>
    </el-dropdown-menu>
  </el-dropdown>
</template>

<script>
import { THEME } from '@/store'
import THEME_MAP from '@/store/theme-map'
import { mapState } from 'vuex'
import { LocalStorageUtils } from '@/utils/storage'

export default {
  name: 'theme-switch',
  computed: {
    ...mapState(['theme']),
  },
  data () {
    return {
      themeList: Object.values(THEME_MAP),
    }
  },
  methods: {
    onThemeItemClick (i) {
      this.$store.commit(THEME, i)
      LocalStorageUtils.writeJSON(THEME, i)
      // TODO: 调用后台 api 接口将主题设置保存到数据库中
    },
  },
}
</script>

<style lang="less" scoped>
.theme-switch {
  vertical-align: top;

  .icon.icon-tshirt {
    font-size: 24px;
  }

  .theme-item {
    color: #fff;
    display: flex;
    align-items: center;

    i.circle {
      @size: 20px;
      display: inline-block;
      vertical-align: top;
      width: @size;
      height: @size;
      border-radius: 50%;
    }
  }
}
</style>

2.9 将当前皮肤的值,置于 DOM 中

将当前皮肤主题的值作为 class 置于 body 即可。

示例:

// src/app.vue

<template>
  <div id="app" :class="[theme.value]">
    <router-view></router-view>
  </div>
</template>

<script>
import { mapState } from 'vuex'

export default {
  name: 'app',
  computed: {
    ...mapState(['theme']),
  },
  watch: {
    theme (newTheme, oldTheme) {
      this.removeThemeFromBody(oldTheme.value)
      this.addThemeToBody(newTheme.value)
    },
  },
  components: {},
  created () {},
  mounted () {
    this.addThemeToBody(this.theme.value)
  },
  methods: {
    addThemeToBody (themeValue) {
      document.body.classList.add(themeValue)
    },
    removeThemeFromBody (themeValue) {
      document.body.classList.remove(themeValue)
    },
  },
}
</script>

<style lang="less" scoped>
#app {
  width: 100%;
  height: 100%;
}
</style>

效果:

<body class="red"></body>

3. 新增皮肤主题

此小节将描述添加主题的具体步骤。

3.1 编写主题文件

// src/assets/styles/themes/green.less

示例:

body.green {
  @primary-color: #00a854;
  @button-text-color: #fff;

  .themeMixins();
}

3.2 注册主题文件

src/assets/styles/index.less引入主题文件即可。

示例:

@import './theme-mixins.less';
@import './themes/green.less';

3.3 添加主题配置

// src/store/theme-map.js

export default {
  // ...
  GREEN: {
    value: 'green',
    name: '绿色主题',
    color: '#00a854',
  },
  // ...
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿祥_csdn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值