vue3笔记(38-1)vue3项目换肤
新需求,项目需要换肤和多语言支持。本篇介绍如何在vue3项目中实现换肤功能。
背景
通常,前端一键换肤功能需要通过使用 CSS 样式表来定义不同的主题样式,然后通过 JavaScript 来控制切换不同的样式表,以达到换肤的效果。用户在点击换肤按钮或者选择不同的主题选项后,页面会立即应用新的样式,从而改变界面的外观。这种功能在很多网站、应用中都有广泛的应用,特别是一些内容丰富、用户群体广泛的平台,以满足不同用户对于外观风格的偏好。
思路
切换主题和皮肤,主要是背景色、字体颜色的替换。分别设置黑暗和明亮两个主题文件夹,当触发主题变化时,调用不同的样式文件。
当前项目中,文件结构如下:
|– assets/scss
|– dark
|– index.scss
|– element-variables.scss
|– light
|– index.scss
|– element-variables.scss
|– common.scss
|– common-ev.scss
|– transition.scss
其中,common.scss
用于全局样式,common-ev.scss
用于全局 element 组件样式修改配置,transition.scss
用于动画效果。dark 和 light 目录下分别存放了黑暗和明亮主题样式和变量文件。index.scss
用于主题样式,element-variables.scss
用于对应主题 element 组件特殊配置。
实现
分别按照设计图设置黑暗和明亮主题的颜色,在 dark 文件下的所有样式都用
.dark
作为最外层包裹。1
2
3
4
5
6
7
8
9
10
11
12.dark {
:root {
--el-text-color-primary: #ffffff;
--el-text-color-regular: #ffffff;
--el-table-text-color: #ffffff;
}
.el-table {
--el-table-header-text-color: rgba(173, 202, 255, 0.8) !important;
--el-table-header-bg-color: #2a3144 !important;
--el-table-row-hover-bg-color: #1f202f !important;
}
}分别在
index.scss
中引入对应的主题变量1
@use './element-variables.scss' as *;
common.scss
中引入其他全局样式文件1
2@use './common-ev.scss' as *;
@use './transition.scss' as *;在
main.ts
中引入通用样式文件和两个主题样式文件。1
2
3import '@/assets/scss/common.scss'
import '@/assets/scss/dark/index.scss'
import '@/assets/scss/light/index.scss'App.vue
中初始化主题,本项目初始化为明亮。主题色会影响到全局样式(侧栏导航)和element组件样式(el-tabs、el-tabs、el-pagination)。1
2
3
4
5import { handleBackgroundStyle, handleThemeStyle } from './utils/theme'
handleBackgroundStyle(['#EEEEEE', '#cccccc', '#bbbbbb'])
handleThemeStyle('#2196f3')/utils/theme.ts
1
2
3
4
5
6
7
8
9
10
11// 处理背景色
export function handleBackgroundStyle(background: any) {
document.documentElement.style.setProperty('--el-bg-color', background[0])
document.documentElement.style.setProperty('--el-bg-color-1', background[1])
document.documentElement.style.setProperty('--el-bg-color-2', background[2])
}
// 处理主题样式
export function handleThemeStyle(theme: any) {
document.documentElement.style.setProperty('--el-color-primary', theme)
}document.documentElement
代表的是文档对象模型(DOM)中的根元素,即 HTML 文档中的元素。style.setProperty
用于在 JavaScript 中设置元素样式。 具体来说,document.documentElement.style.setProperty
方法可以用于动态地设置根元素的 CSS 样式属性。它接受两个参数:
属性名: 第一个参数是要设置的 CSS 属性名称,例如 “color”, “font-size”, “background-color” 等等。
属性值: 第二个参数是要为属性设置的值,可以是字符串或者变量,表示对应样式属性的值。
这种方法可以用于动态改变页面的整体样式,在实现一键换肤功能时非常有用。通过 JavaScript 动态地调用setProperty
方法,可以实现在用户操作后改变整个页面的外观,从而实现换肤的效果。设置一个按钮,点击切换主题。通过在body标签上添加class=“dark”或“”来切换主题。
layout/components/navbar/ThemeSwitch.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28<template>
<button
type="button"
role="switch"
aria-label="切换深色模式"
aria-checked="false"
@click="toggleDarkMode"
>
</button>
</template>
<script setup lang="ts">
import { handleBackgroundStyle, handleThemeStyle } from '@/utils/theme'
const toggleDarkMode = () => {
const body = document.documentElement as HTMLElement
const classValue = body.getAttribute('class')
if (classValue === 'dark') {
body.setAttribute('class', '')
handleBackgroundStyle(['#EEEEEE', '#cccccc', '#000000'])
handleThemeStyle('#000000')
} else {
body.setAttribute('class', 'dark')
handleBackgroundStyle(['#151723', '#2a3144', '#313955'])
handleThemeStyle('#ff7f02')
}
}
</script>其他替换项目
(1)所有的按钮图标需要替换,深色模式为白色,明亮模式为黑色。
(2)tooltip、dialog、message等组件的样式需要替换。.el-button–primary
(3)图表颜色替换。components/echart/src/template/index.ts 中,修改getEchartsColor判断
优化
每次点击时切换主题,同时要在页面刷新时保持原有主题,所以我们做一个简单的缓存优化。utils/cookie.ts
1 |
|
src/store/modules/app.ts
1 |
|
layout/components/navbar/ThemeSwitch.vue
1 |
|
App.vue
中从缓存中获取初始化主题。
1 |
|
问题
echarts 图表在切换主题时需要重绘,这里尝试了
components/echart/src/echart.vue
1
2
3
4
5
6watch(
() => appStore.theme,
(theme) => {
initChart(theme)
}
)没有成功,感觉这个theme和颜色不是同一个。
在
components/echart/src/template/index.ts
中,观察 theme 变化,修改getEchartsColor
判断,切换主题时,图表颜色也会变化。但是仔细一看,变化只是跟着背景变了,实际的颜色配置还是没获取成功。。。原因是图表色卡或者失败。。。
若将 lineOptions: any = ref({}),colors.value 改变时,背景颜色无法跟着变。1
2
3
4
5
6
7
8
9
10
11const colors = ref<EchartsTheme>()
watch(
() => appStore.theme,
(theme) => {
colors.value = getEchartsColor(theme === 'dark')
console.log(
colors.value.bgColor,
lineOptions.value.backgroundColor
) // 颜色变化,但是背景颜色没有变化
}
)
按需加载
对于需要按需加载的项目,尝试直接将dark
和light
文件夹引入,原vite.config.ts
配置如下:
1 |
|
执行后,有如下报错[sass] This module was already loaded, so it can't be configured using "with".
当只引入一个文件夹内容时,报错消失,初步判断是有重复调用。
总结
- 为了避免后期改动麻烦,建议在项目初始阶段(或任一新模块开发初期)就按照配色规范来编写 css 代码。
- 避免在 html 中直接编写 style=”” 此类代码,以防止后期修改样式时出现问题。
- 模块中尽量不写和颜色相关的 !important,以防止样式修改时造成意想不到的问题。
1 |
|
附录
vue3如何实现换肤效果(精简版)
两种最简单的方式教会你如何实现前端一键换肤!
CSS — BEM 命名规范
element-plus 主题色配置
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!