前端主题切换方案

1,485 阅读3分钟

前端主题切换方案

前言

在业务中遇到一个将迭代过n多个版本的vue项目做主题切换,看过很多主题切换方案,也实践过一些,面对各种样式引入的方式

内联方式

<div id="app">
    <h2 :style="{'background-color': 'green'}">test1</h2>
    <h2 :style="styleObj1">test2</h2>
    <h2 :style="[styleObj1,styleObj2]">test3</h2>
</div>

style标签

    <!-- css -->
    <style>
        .green {
            background-color: green;
        }
        .big {
            font-weigth: 200;
        }
        .active {
            letter-spacing: 0.5em;
        }
    </style>

在组件中引入

<script>
    import '../static/css/global.css' /*引入公共样式*/
</script>
<style scoped >

 @import './../static/css/global.css'; /*引入公共样式*/
 @import "../css/style.css";

</style>

由于样式风格不一且分散,主题做起来很是头疼.那么有没有一种,即不用大量改动代码,又简单轻松的方式呢? 最终集多种方案于一体,实现了自有的一套主题方案,将主题全局覆盖式,插件式.

方案

主题配置

根据项目需求任意配置主题风格

config.js

const themes = [
    {
        key: 'day',
        name: '白天',
        mainClassName: 'app-theme-day'
    },
    { 
        key: 'night',
        name: '黑夜',
        mainClassName: 'app-theme-night'
    },
    {
        key: 'technology-blue',
        name: '科技蓝',
        mainClassName: 'app-theme-technology-blue'
    },
]

export default themes
主题设置

提供初始化主题和切换主题调用方法

setting.js

import Vue from 'vue'
import { get } from 'lodash'
import { message } from 'ant-design-vue/es'
import themes from './config'

const setBodyClass = theme => {
    const matchedItem = themes.filter(a => a.key === theme)
    if (!matchedItem.length) {
        message.error(`组件库内并无该主题${theme},请查看文档再选择主题种类`)
        return
    }

    // 设置body类
    const body = document.querySelector('body')

    for (const item of themes) {
        const { key, mainClassName } = item
        body.classList.remove(mainClassName)
    }

    body.classList.add(matchedItem[0].mainClassName)
    window.localStorage.setItem('APP_THEME', theme)
}

const initTheme = () => {
    const defaultTheme = get(Vue.prototype.$app_config, 'theme.key')
    // 初始化获取主题
    const currentTheme = window.localStorage.getItem('APP_THEME') || defaultTheme || 'technology-blue'
    if (!currentTheme) {
        return
    }

    setBodyClass(currentTheme)
}

const changeTheme = theme => {
    if (!theme) {
        return
    }
    
    setBodyClass(theme)
}



export { initTheme, changeTheme, themes }

切换方式很简单,即在body标签上添加对应主题的class

引入主题

在项目入口处引入样式

main.js

import '@/style/index'

样式目录结构

|-- style
	|-- themes
		|-- day.less
		|-- index.less
		|-- night.less
		|-- technology-blue.less
	|-- views
		|-- header
			|-- assets
                |-- header-swsj-day.png
                |-- header-swsj-night.png
			|-- index.less
		
		...
		
		|-- 各自负责的组件模块
	|-- index.js
	|-- index.less

style/index.js

import './index.less'

style/index.less


// 引入UI组件库样式
@import 'xx-view-vue/src/styles/index.less';
// 项目中各自负责的组件模块主题样式入口
@import './themes/index';

// 主题入口 不同主题类 对应 @/theme/config.js 配置中自定义的 `mainClassName`,也是添加到`body`标签的`class`
.app-theme-day {
  .theme-day();
  .xx-view-theme-day();
}

.app-theme-night {
  .theme-night();
  .xx-view-theme-night();
}

.app-theme-technologyBlue {
  .theme-technologyBlue();
  .xx-view-theme-night();
}

应用了 less的[Mixins](Less 快速入门 | Less.js 中文文档 - Less 中文网 (bootcss.com))

style/themes/index.less

@import './day.less';
@import './night.less';
@import './technologyBlue.less';

style/themes/day.less

@import '../views/header/index';

.theme-day() {
    @theme: day;

    // -------- Colors -----------
    @base-color: #ffffff;
    @font-base-color: #333333;
    
    // -------- views\solution\index.vue  -----------
    // 顶部背景
    @bg-image-mask: rgba(255,255,255,0);
    .solution-header-bg(@theme, @bg-image-mask);
    // logo
    .theme-logo(@theme);
}

style/themes/night.less

@import '../views/header/index';

.theme-night() {
    @theme: night;

    // -------- Colors -----------
    @base-color: #0f1920;
    @font-base-color: #e0e0e0;
    
    // -------- views\solution\index.vue  -----------
    // 顶部背景
    @bg-image-mask: linear-gradient(180deg, #080808 0%, rgba(0,0,0,0.18) 70%, rgba(69,69,69,0.00) 100%);
    .solution-header-bg(@theme, @bg-image-mask);
    // logo
    .theme-logo(@theme);
}

style/views/hander/index.less

// 顶部背景
.solution-header-bg(@theme, @bg-image-mask) {
  @solution-header-bg-url: './assets/header-swsj-@{theme}.png';
  @solution-header-bg: transparent url(@solution-header-bg-url) no-repeat top center;
  
  .solution-header-bg {
    background: @solution-header-bg!important;
    backdrop-filter: blur(1px);

    &:before {
      content: ' ';
      position: absolute;
      width: 100%;
      height: 100%;
      z-index: -1;
      top: 0;
      left: 0;
      pointer-events: none;
      background-image: @bg-image-mask!important;
      background-size: 100% 100%;
    }
  }
}

// logo theme-logo
.theme-logo(@theme) {
  @theme-logo-url: './assets/logo-swsj-@{theme}.png';

  .theme-logo {
    content:url(@theme-logo-url)!important;
  }
}

solution-header-bg,theme-logo即需要修改主题的组件类名

header/index.vue

<template>
    <div class="solution-header solution-header-bg">
    </div>
</template>  

<style lang="less" scoped>
.solution-header {
    position: absolute;
    top: 0;
    left: 0;
    height: 70px;
    // width: calc(~'100% - 20px');
    width: 100vw;
    z-index: 110;
    display: flex;
    justify-content: space-between;
    // margin: 0 10px;
    // background: linear-gradient(0, transparent, rgba(0, 0, 0, 0.3));
    background-size: 100% 100%;
}    
</style>    

不影响各自开发自己的模块组件,主题只需将之前写的样式,复制到 @/style下,再设置需要响应切换主题所需要的样式,提取变量

初始化主题

调用initTheme

App.vue

<template>
    <div id="app">
    </div>
</template>
<script>
import { initTheme } from '@/themes/setting.js'

export default {
    mounted() {
        initTheme()
    },
}
</script>
切换主题

调用changeTheme

<script>
export default {    
    methods: {
        onChangeTheme(e) {
            const val = e.target.value
            changeTheme(val)
        },
    },
} 
</script>

最后

以上就是主题切换方案,第一次发帖,不知道我讲清楚了没有[doge]