Esquema de cambio de tema de la página principal

Solución 1: importación dinámica de etiquetas de enlace


El método consiste en preparar varios conjuntos de archivos de estilo de tema CSS por adelantado y, cuando sea necesario, crear una etiqueta de enlace para cargar dinámicamente en la etiqueta principal o cambiar dinámicamente el atributo href de la etiqueta de enlace.

ventaja:

  • Se implementó la carga a pedido y se mejoró el rendimiento de la carga de la primera pantalla.

defecto:

  • Cargue archivos de estilo dinámicamente. Si el archivo es demasiado grande y la condición de la red es deficiente, puede haber un retraso en la carga, lo que resulta en un cambio de estilo irregular.

  • Si no se define correctamente en la hoja de estilo del tema, habrá problemas prioritarios

  • Cada estilo de tema está codificado de forma rígida y también es muy problemático modificar o agregar un tema a una hoja de estilo de tema más adelante.

Solución 2: presente todos los estilos de tema por adelantado y cambie los nombres de las clases


Esta solución es similar a la primera. Para resolver el problema de cargar archivos de estilo repetidamente, todos los estilos se introducen por adelantado y el nombre de la clase del elemento raíz especificado se reemplaza cuando es necesario cambiar el tema, lo que equivale a directamente anulando el estilo Los siguientes estilos se reemplazan uniformemente. El método básico es el siguiente:

/* day样式主题 */
body.day .box {
color: #f90;
background: #fff;
}
/* dark样式主题 */
body.dark .box {
color: #eee;
background: #333;
}
.box {
width: 100px;
height: 100px;
border: 1px solid #000;
}
<div class="box">
<p>hello</p>
</div>
<p>  
选择样式:
<button onclick="change('day')">day</button>
<button onclick="change('dark')">dark</button>
</p>
functionchange(theme) {
document.body.className = theme;
}

ventaja:

  • No es necesario volver a cargar el archivo de estilo y no habrá demoras al cambiar de estilo

defecto:

  • Cuando se carga la primera pantalla, se sacrificará algo de tiempo para cargar recursos de estilo

  • También puede haber problemas de prioridad si no se definen correctamente en la hoja de estilo del tema.

  • Cada estilo de tema está codificado de forma rígida y también es muy problemático modificar o agregar un tema a una hoja de estilo de tema más adelante.

Solución 3: variable CSS + cambio de nombre de clase


Referencia de inspiración: sitio web oficial de Vue3

Hay un botón de cambio de modo oscuro en el sitio web oficial de Vue3, que cambiará sin problemas después de hacer clic. Aunque también hay una función v-bind en Vue3 que puede realizar un enlace de estilo dinámico, después de la observación, el sitio web oficial de Vue no adopta esta solución. Para Vue3 v La función -bind se elaborará en el siguiente plan.

La idea general es similar a la solución 2. Sigue siendo cargar el archivo de estilo por adelantado y reemplazar el nombre de clase del elemento raíz especificado al cambiar. Sin embargo, lo que es relativamente flexible aquí es que las variables CSS se definen en el ámbito raíz de forma predeterminada, y solo necesita cambiar los valores correspondientes de las variables CSS en diferentes temas.

Por cierto, el esquema de color: oscuro también se usa en el sitio web oficial de Vue3; la barra de desplazamiento del sistema está configurada en modo negro para que el estilo sea más uniforme.

html .oscuro {

combinación de colores: oscuro;

}

El plan de implementación es el siguiente:

/* 定义根作用域下的变量 */
:root {  --theme-color: #333;  --theme-background: #eee;}
/* 更改dark类名下变量的取值 */
.dark{  --theme-color: #eee;  --theme-background: #333;}
/* 更改pink类名下变量的取值 */
.pink{  --theme-color: #fff;  --theme-background: pink;}
.box {transition: all .2s;width: 100px;height: 100px;border: 1px solid #000;
/* 使用变量 */
color: var(--theme-color);
background: var(--theme-background);
}

El rendimiento es el siguiente:

ventaja:

  • No es necesario volver a cargar el archivo de estilo y no habrá demoras al cambiar de estilo

  • Simplemente use var () para vincular variables donde necesita cambiar el tema, no hay problema de prioridad

  • Agregar o modificar temas es conveniente y flexible, solo necesita agregar o modificar variables CSS, y se reemplazará automáticamente en el lugar donde var() vincula las variables de estilo

defecto:

  • Compatibilidad con IE (insignificante)

  • Cuando se carga la primera pantalla, se sacrificará algo de tiempo para cargar recursos de estilo

方案4:Vue3新特性(v-bind)


虽然这种方式存在局限性只能在Vue开发中使用,但是为Vue项目开发者做动态样式更改提供了又一个不错的方案。

简单用法

<script setup>
// 这里可以是原始对象值,也可以是ref()或reactive()包裹的值,根据具体需求而定
const theme = {    color: 'red'  }
</script>
<template>
<p>hello</p>
</template>
<style scoped>
p {color: v-bind('theme.color');
}</style>

Vue3中在style样式通过v-bind()绑定变量的原理其实就是给元素绑定CSS变量,在绑定的数据更新时调用CSSStyleDeclaration.setProperty更新CSS变量值。

前面方案3基于CSS变量绑定样式是在:root上定义变量,然后在各个地方都可以获取到根元素上定义的变量。现在的方案我们需要考虑的问题是,如果是基于JS层面如何在各个组件上优雅地使用统一的样式变量?

我们可以利用Vuex或Pinia对全局样式变量做统一管理,如果不想使用类似的插件也可以自行封装一个hook,大致如下:

// 定义暗黑主题变量
exportdefault { 
 fontSize: '16px', 
 fontColor: '#eee',
 background: '#333',};
// 定义白天主题变量
exportdefault {  
fontSize: '20px',  
fontColor: '#f90',  
background: '#eee',
};
import { shallowRef } from'vue';
// 引入主题
import theme_day from'./theme_day';
import theme_dark from'./theme_dark';
// 定义在全局的样式变量
const theme = shallowRef({});
exportfunctionuseTheme() {
// 尝试从本地读取
const localTheme = localStorage.getItem('theme'); 
theme.value = localTheme ? JSON.parse(localTheme) : theme_day;constsetDayTheme = () => {    
theme.value = theme_day;  
};
constsetDarkTheme = () => {   
 theme.value = theme_dark;
  };
return {    
theme,    
setDayTheme,    
setDarkTheme,
  };
}

使用自己封装的主题hook

<script setuplang="ts">
import { useTheme } from'./useTheme.ts';
importMyButtonfrom'./components/MyButton.vue';
const { theme } = useTheme();
</script>
<template>
<div class="box">
<span>Hello</span>
</div>
<my-button />
</template>
<style lang="scss">
.box {
width: 100px;height: 100px;
background: v-bind('theme.background');
color: v-bind('theme.fontColor');
font-size: v-bind('theme.fontSize');
}
</style>
<script setuplang="ts">
import { useTheme } from'../useTheme.ts';
const { theme, setDarkTheme, setDayTheme } = useTheme();constchange1 = () => {setDarkTheme();
};
constchange2 = () => {setDayTheme();};
</script>
<template>
<button class="my-btn" @click="change1">dark</button>
<button class="my-btn" @click="change2">day</button>
</template>
<style scopedlang="scss">
.my-btn {
color: v-bind('theme.fontColor');
background: v-bind('theme.background');
}
</style>

其实从这里可以看到,跟Vue的响应式原理一样,只要数据发生改变,Vue就会把绑定了变量的地方通通更新。

优点:

  • 不用重新加载样式文件,在样式切换时不会有卡顿

  • 在需要切换主题的地方利用v-bind绑定变量即可,不存在优先级问题

  • 新增或修改主题方便灵活,仅需新增或修改JS变量即可,在v-bind()绑定样式变量的地方就会自动更换

缺点:

  • IE兼容性(忽略不计)

  • 首屏加载时会牺牲一些时间加载样式资源

  • 这种方式只要是在组件上绑定了动态样式的地方都会有对应的编译成哈希化的CSS变量,而不像方案3统一地就在:root上设置(不确定在达到一定量级以后的性能),也可能正是如此,Vue官方也并未采用此方式做全站的主题切换

方案5:SCSS + mixin + 类名切换


主要是运用SCSS的混合+CSS类名切换,其原理主要是将使用到mixin混合的地方编译为固定的CSS以后,再通过类名切换去做样式的覆盖,实现方案如下:

定义SCSS变量

/* 字体定义规范 */
$font_samll:12Px;$font_medium_s:14Px;$font_medium:16Px;$font_large:18Px;
/* 背景颜色规范(主要) */
$background-color-theme: #d43c33;
//背景主题颜色默认(网易红)
$background-color-theme1: #42b983;
//背景主题颜色1(QQ绿)
$background-color-theme2: #333;
//背景主题颜色2(夜间模式)
/* 背景颜色规范(次要) */
$background-color-sub-theme: #f5f5f5;
//背景主题颜色默认(网易红)
$background-color-sub-theme1: #f5f5f5;
//背景主题颜色1(QQ绿)
$background-color-sub-theme2: #444;
//背景主题颜色2(夜间模式)
/* 字体颜色规范(默认) */
$font-color-theme : #666;
//字体主题颜色默认(网易)
$font-color-theme1 : #666;
//字体主题颜色1(QQ)
$font-color-theme2 : #ddd;
//字体主题颜色2(夜间模式)
/* 字体颜色规范(激活) */
$font-active-color-theme : #d43c33;
//字体主题颜色默认(网易红)
$font-active-color-theme1 : #42b983;
//字体主题颜色1(QQ绿)
$font-active-color-theme2 : #ffcc33;
//字体主题颜色2(夜间模式)
/* 边框颜色 */
$border-color-theme : #d43c33;
//边框主题颜色默认(网易)
$border-color-theme1 : #42b983;
//边框主题颜色1(QQ)
$border-color-theme2 : #ffcc33;
//边框主题颜色2(夜间模式)
/* 字体图标颜色 */
$icon-color-theme : #ffffff;
//边框主题颜色默认(网易)
$icon-color-theme1 : #ffffff;
//边框主题颜色1(QQ)
$icon-color-theme2 : #ffcc2f;
//边框主题颜色2(夜间模式)
$icon-theme : #d43c33;
//边框主题颜色默认(网易)
$icon-theme1 : #42b983;
//边框主题颜色1(QQ)
$icon-theme2 : #ffcc2f;
//边框主题颜色2(夜间模式)

定义混合mixin

@import"./variable.scss";
@mixin bg_color(){
background: $background-color-theme;  
[data-theme=theme1] & {
background: $background-color-theme1;  
}  
[data-theme=theme2] & {
background: $background-color-theme2;
  }}
@mixin bg_sub_color(){
background: $background-color-sub-theme;  
[data-theme=theme1] & {background: $background-color-sub-theme1;  
}  
[data-theme=theme2] & {
background: $background-color-sub-theme2;  
}}
@mixin font_color(){color: $font-color-theme;  
[data-theme=theme1] & {color: $font-color-theme1;  
}  
[data-theme=theme2] & {
color: $font-color-theme2;  
}}
@mixin font_active_color(){
color: $font-active-color-theme;  
[data-theme=theme1] & {
color: $font-active-color-theme1;  
}  
[data-theme=theme2] & {
color: $font-active-color-theme2;  
}}
@mixin icon_color(){
color: $icon-color-theme;    
[data-theme=theme1] & {
color: $icon-color-theme1;    
}    
[data-theme=theme2] & {
color: $icon-color-theme2;    
}}
@mixin border_color(){
border-color: $border-color-theme;  
[data-theme=theme1] & {
border-color: $border-color-theme1;  
}  
[data-theme=theme2] & {border-color: $border-color-theme2;  
}}
<template>
<div class="header" @click="changeTheme">
<div class="header-left">
<slot name="left">左边</slot>
</div>
<slot name="center"class="">中间</slot>
<div class="header-right">
<slot name="right">右边</slot>
</div>
</div>
</template>
<script>
exportdefault {    
name: 'Header',    
methods: {      
changeTheme () {
document.documentElement.setAttribute('data-theme', 'theme1')      
}    
}  
}</script>
<style scopedlang="scss">
@import"../assets/css/variable";
@import"../assets/css/mixin";
.header{width: 100%;height: 100px;
font-size: $font_medium;
@include bg_color();
}
</style>

这种方案最后得到的结果与方案2类似,只是在定义主题时由于是直接操作的SCSS变量,会更加灵活。

优点:

  • 不用重新加载样式文件,在样式切换时不会有卡顿

  • 在需要切换主题的地方利用mixin混合绑定变量即可,不存在优先级问题

  • 新增或修改主题方便灵活,仅需新增或修改SCSS变量即可,经过编译后会将所有主题全部编译出来

缺点:

  • 首屏加载时会牺牲一些时间加载样式资源

方案6:CSS变量+动态setProperty


此方案较于前几种会更加灵活,不过视情况而定,这个方案适用于由用户根据颜色面板自行设定各种颜色主题,这种是主题颜色不确定的情况,而前几种方案更适用于定义预设的几种主题。

方案参考:vue-element-plus-admin

主要实现思路如下:

只需在全局中设置好预设的全局CSS变量样式,无需单独为每一个主题类名下重新设定CSS变量值,因为主题是由用户动态决定。

:root {  --theme-color: #333;  --theme-background: #eee;}

定义一个工具类方法,用于修改指定的CSS变量值,调用的是CSSStyleDeclaration.setProperty

exportconstsetCssVar = (prop: string, val: any, dom = document.documentElement) => {  dom.style.setProperty(prop, val)}

在样式发生改变时调用此方法即可

setCssVar('--theme-color', color)

优点:

  • 不用重新加载样式文件,在样式切换时不会有卡顿

  • 仔细琢磨可以发现其原理跟方案4利用Vue3的新特性v-bind是一致的,只不过此方案只在:root上动态更改CSS变量而Vue3中会将CSS变量绑定到任何依赖该变量的节点上。

  • 需要切换主题的地方只用在:root上动态更改CSS变量值即可,不存在优先级问题

  • 新增或修改主题方便灵活

缺点:

  • IE兼容性(忽略不计)

  • 首屏加载时会牺牲一些时间加载样式资源(相对于前几种预设好的主题,这种方式的样式定义在首屏加载基本可以忽略不计)

方案总结


说明:两种主题方案都支持并不代表一定是最佳方案,视具体情况而定。

方案/主题样式

固定预设主题样式

主题样式不固定

方案1:link标签动态引入

√(文件过大,切换延时,不推荐)

×

方案2:提前引入所有主题样式,做类名切换

×

方案3:CSS变量+类名切换

√(推荐)

×

方案4:Vue3新特性(v-bind)

√(性能不确定)

√(性能不确定)

方案5:SCSS + mixin + 类名切换

√(推荐,最终呈现效果与方案2类似,但定义和使用更加灵活)

×

方案6:CSS变量+动态setProperty

√(更推荐方案3)

√(推荐)

本文转载,仅供自己学习参考使用!!!

Supongo que te gusta

Origin blog.csdn.net/qq_45144083/article/details/129281248
Recomendado
Clasificación