scss配合vue实现换肤效果

1,569 阅读2分钟

基于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>

最后编译出来的结果:

image-20230522141746976

简易的转化:

.list {
  [data-theme='dark'] & {
    width: 100px;
    height: 100px;
    background-color: red;
  }
}

它本质上,先选中了.list类选择器,然后内部重新定义必须是在属性选择器[data-theme='dark']下 的.list类选择器样式才生效。

因此,就可以用这种方式定义默认的样式:

image-20230522142628441

效果如图:

image-20230522142652007image-20230522142700249image-20230522143051692转存失败,建议直接上传图片文件

这是因为复杂选择器的权重是所有选择器之和,大于类选择器,当然也可以通过!important,让下面那个永远生效

SCSS的一些问题

image-20230522141031756

[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:可以遍历定义的映射,获取到keykey和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>

image-20230706145231520

案例

$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>