vue-cli3实现主题换肤功能

vue-cli3实现主题换肤功能

参考自https://www.cnblogs.com/webSciprt/p/ji-yuwebpack4vuecli3xiang-mu-de-huan-fu-gong-neng.html
在这位网友的代码基础上进行了些许改动并附上demo
主题换肤demo 码云 https://gitee.com/hanlingsha/theme-demo

实现效果

vue-cli3实现主题换肤功能_第1张图片
vue-cli3实现主题换肤功能_第2张图片
vue-cli3实现主题换肤功能_第3张图片
vue-cli3实现主题换肤功能_第4张图片

主要依赖

style-loader vue-cli3默认采用vue-style-loader无法实现我们的需求
stylus stylus-loader 我使用了cube-ui,默认集成了stylus,大家也可以采用css或者sass

核心代码

vue.config.js (cube-ui会为我们在根目录下创建,也可以自己创建,名字必须保持一致)

chainWebpack: config => {
    const stylus = config.module
      .rule('stylus')
      .toConfig()
    const theme = {
      ...stylus.oneOf[3],
      test: /\.styl$/
    }
    theme.use = [...theme.use]
    theme.use[0] = {
      loader: 'style-loader',
      options: {
        injectType: 'lazyStyleTag'  // 原文中采用了useable,在我使用的过程中发生报错,查阅英文文档修正
      }
    }
    config.module
      .rule('stylus')
      .merge({
        oneOf: [theme]
      })
  }

src/common/theme.js

let obj = {}
let current = null

const theme = {
  dark () {
    if (!obj.dark) {
      obj.dark = import('../assets/theme/theme.dark.styl')
    }
    return obj.dark
  },

  light () {
    if (!obj.light) {
      obj.light = import('../assets/theme/theme.light.styl')
    }
    return obj.light
  }
}

async function setTheme (name) {
  if (theme[name]) {
    let style = await theme[name]()
    if (current) {
      current.unuse()
    }
    style.use()
    current = style
  }
}

export default setTheme

src/App.vue

<script>
import { mapMutations } from 'vuex'
import setTheme from './common/theme'
export default {
  name: 'App',
  methods: {
    ...mapMutations([
      'setThemeIndex' //主要用于判断使用中的主题,设置样式,如上图中的小绿点
    ])
  },
  created () {
    setTheme('light') // 默认使用浅色主题
    this.setThemeIndex(1)
  }
}
</script>

Theme.vue

<template>
  <div class="page">
    <v-header title="主题换肤"></v-header>
    <div class="wrapper">
      <div class="div-theme-list">
        <div class="div-theme" v-for="item in themes" :key="item.index" @click="clickHandler(item.index)">
          <div :class="themeColor(item.theme)">
            <div v-show="item.index === theme" class="index"></div>
          </div>
          <div class="div-theme-title c-center-flex">深色主题</div>
        </div>
      </div>
    </div>
    <router-link tag="div" to="/" class="div-back c-center-flex">首页</router-link>
  </div>
</template>

<script>
import { mapState, mapMutations } from 'vuex'
import VHeader from '@/components/v-header'
import setTheme from '../common/theme'
export default {
  name: 'Theme',
  components: { VHeader },
  data () {
    return {
      themes: [
        {
          index: 1,
          theme: 'light',
          name: '默认浅色主题'
        }, {
          index: 2,
          theme: 'dark',
          name: '深色主题'
        }
      ]
    }
  },
  computed: {
    themeColor (theme) {
      return (theme) => {
        let arr = ['div-theme-color']
        arr.push(theme)
        return arr
      }
    },
    ...mapState([
      'theme'
    ])
  },
  methods: {
    clickHandler (index) {
      if (index === 1) {
        setTheme('light')
      }
      if (index === 2) {
        setTheme('dark')
      }
      this.setThemeIndex(index)  // 设置使用中的主题,于App.vue中的代码呼应
    },
    ...mapMutations([
      'setThemeIndex'
    ])
  }
}
</script>

后面的话

对于用户和用户所选主题的绑定,采用localstorage和数据库存储都是可以的。
对于样式来说,大家可以参考我的demo,我的思路是编写公用的样式文件,然后每个主题都拥有一个自己的样式文件,引入公用文件,对颜色进行覆盖
对于可用主题的存储,我个人倾向文件存储,并导出一个数组,采用vuex或者localstorage来管理当前使用主题
注意刷新页面时,vuex会被重置
如果大家还有其他问题,可以评论

你可能感兴趣的:(vue)