前端主题切换方案
前言
在业务中遇到一个将迭代过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]