基于Vue、Scss实现换肤效果
原理:
通过给html根标签添加data-theme的自定义属性,值为对应的主题名,再根据不同的主题名,定义对应的属性选择器,从而实现换肤效果。
代码:
$themes: (
light: (
menu_back_ground: #fff,
font_color: #000000,
),
dark: (
menu_back_ground: #370d55,
font_color: #fff,
),
);
//遍历$themes变量,将子map升级为全局。让函数中可以调用这个$theme-map
@mixin themeify {
@each $theme-name, $theme-map in $themes {
$theme-map: $theme-map !global;
//根标签里添加了data-theme='dark' 最后选择器变为[data-theme='dark'] .xxx{}
[data-theme="#{$theme-name}"] & {
@content;
}
}
}
//根据key获取对应的样式值 最后返回的就是#fff
@function themed($key) {
@return map-get($theme-map, $key);
}
@mixin menu_back_ground {
@include themeify {
background-color: themed("menu_back_ground");
}
}
组件内调用
<template>
<div>
<button @click="change">点击</button>
<div class="menu"></div>
<div class="list"></div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted, watch } from 'vue'
const change = () => {
let html = document.querySelector('html')
html?.setAttribute('data-theme', 'dark')
}
</script>
<style lang="scss" scoped>
@import '@/style/theme.scss';
.menu {
width: 200px;
height: 400px;
@include menu_back_ground;
}
</style>
最后编译出来的结果:
简易的转化:
.list {
[data-theme='dark'] & {
width: 100px;
height: 100px;
background-color: red;
}
}
它本质上,先选中了.list类选择器,然后内部重新定义必须是在属性选择器[data-theme='dark']下 的.list类选择器样式才生效。
因此,就可以用这种方式定义默认的样式:
效果如图:
这是因为复杂选择器的权重是所有选择器之和,大于类选择器,当然也可以通过!important,让下面那个永远生效
SCSS的一些问题
[data-theme='#{$theme-name}'] & 这个&代表的是上一级的选择器。本质上,先选中了.list类选择器,然后内部重新定义必须是在属性选择器[data-theme='dark']下 的.list类选择器样式才生效。
.list {
[data-theme='dark'] & {
width: 100px;
height: 100px;
background-color: red;
}
}
@content:作为占位符。当通过@include themeify调用后,它后面的内容就会传到其中。
案例:
$color: white;
@mixin colors($color: blue) {
background-color: $color;
@content;
border-color: $color;
}
.colors {
@include colors { color: $color; }
}
//编译后
.colors {
background-color: blue;
color: white;
border-color: blue;
}
@mixin menu_back_ground {
@include themeify {
background-color: themed("menu_back_ground");
}
}
//编译后
[data-theme='xxx'] & {
background-color: themed("menu_back_ground");
}
@each in:可以遍历定义的映射,获取到value
!global: 将对应的变量升级到全局,使得其他地方可以直接调用
scss中使用vue的变量
原理:
在组件或标签中,通过:style 绑定将其传递给样式。然后,在 SCSS 中使用 CSS 变量或自定义属性来接收该变量。
注意:
- 只能在
当前元素或子元素的选择器中调用
<main class="data-content" :style="`--content: ${content}`">
<div class="test">123</div>
</main>
//Vue
data() {
return {
isDrag: true,
content: '#eee',
}
},
<style lang="scss" scoped>
//方式①直接使用var(--content)接收
.test {
color:var(--content);
}
//方式②scss变量
$string: var(--content);
.test {
color: $string;
}
</style>
- 注意:绝大多数的时候值不是字符串
<main class="data-content" :style="`--content: ${content}`">
<div class="test">123</div>
</main>
但是,如果需要字符串需要额外加' ',比如说after伪元素
<main class="data-content" :style="`--content:'${content}'`">
<div class="test">123</div>
</main>
案例
$theme-color-map: (
primary: (
bg_color: #409eff,
),
success: (
bg_color: #67c23a,
),
warning: (
bg_color: #f54343,
),
);
@each $theme-name, $item-map in $theme-color-map {
.btn.type-#{$theme-name} {
width: 100px;
height: 30px;
$color: map-get(
$map: $item-map,
$key: bg_color,
);
background-color: $color;
color: #fff;
&:hover {
background-color: lighten($color, 10%);
}
&:active {
background-color: darken($color, 10%);
}
&:disabled {
background-color: lighten($color, 10%);
}
}
}
使用
<div id="app">
<div class="btn type-primary"></div>
<div class="btn type-success"></div>
<div class="btn type-warning"></div>
<img src="/1.png" alt="" />
</div>