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
 3- import '@/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
 5- import { 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
 6- watch(
 () => 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
 11- const 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 协议 ,转载请注明出处!