【sass+css变量实现换肤】

文章目录

需求

假如你最近在开发一套组件库,如何让用户可以换肤呢?

  1. 使用者可以固定自定义主题色、基本色等相关主题
  2. 也支持动态切换,可以只切换单个颜色,也支持全部切换

思路

定义sass或者css变量,所有的颜色都用变量定义,当用户自定义颜色时,通过覆盖或者修改变量完成换肤

组件库css样式设计注意点:

  1. 用bem方式命名:块、元素、修饰符
  2. 结构和皮肤分离

实现

组件

这里拿两个组件举例子,一个是src/myui/mybutton.vue:

html 复制代码
<template>
  <button class="button button-primary">
    <slot></slot>
  </button>
</template>
<script>
export default {
  name: "mybutton",
};
</script>

另一个是src/myui/myheader.vue:

html 复制代码
<template>
<!-- 定义结构和皮肤的类分离开 -->
  <header class="header background-theme">12312</header>
</template>

 
<script>
export default {
  name: "myheader",
};
</script>

手动换肤

定义默认的颜色变量文件src/myui/css/variable.scss:

css 复制代码
$theme-color:red !default;// 默认皮肤都要加default,如果不加default,后面可能无法覆盖这个变量
$button-primary:blue !default;

在main.js中引入默认样式:

html 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import myui from "./myui"
import "./variable.scss"
createApp(App).use(myui).mount('#app')

使用:

html 复制代码
<script setup>
import HelloWorld from "./components/HelloWorld.vue";
import TheWelcome from "./components/TheWelcome.vue";
import { onMounted, ref } from "vue";
</script>

<template>
  <div class="myclass">
    <myheader></myheader>
    <mybutton>1231</mybutton>
  </div>
</template>

<style scoped>
header {
  line-height: 1.5;
}

.logo {
  display: block;
  margin: 0 auto 2rem;
}

@media (min-width: 1024px) {
  header {
    display: flex;
    place-items: center;
    padding-right: calc(var(--section-gap) / 2);
  }

  .logo {
    margin: 0 2rem 0 0;
  }

  header .wrapper {
    display: flex;
    place-items: flex-start;
    flex-wrap: wrap;
  }
}
</style>

src/myui/css/index.scss中定义样式:

css 复制代码
 @import "./variable.scss";// 引入
 .header{
  width: 100%;
  min-height: 30px;
 }
 .button{
  min-width: 40px;
 }
 .background-theme{
   background-color: $theme-color;//使用变量
 }
 .button-primary{
  background-color: $button-primary;
 }

如果是手动换肤,接下来可以新建一个新皮肤的scss文件src/myindex.scss:

css 复制代码
// 编写变量来覆盖变量
$theme-color:pink;
$button-primary:yellow;

@import "@/myui/css/index.scss"// 引入组件库的样式文件,一定要写在最后

然后改变main.js中的引用:

javascript 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import myui from "./myui"
import "./myindex.scss"// 改变引用
createApp(App).use(myui).mount('#app')

动态换肤

如果想实现动态换肤,那么需要改写变量和属性的生成方式,src/myui/css/variable.scss的内容改为:

css 复制代码
//  $theme-color:red !default;// 默认皮肤都要加default,如果不加default,后面可能无法覆盖这个变量
//  $button-primary:blue !default;

// 将变量的声明方式改写成集合的形式
$themes:(// 在scss中,()用来定义对象
    default-theme:(// default-theme是这个对象的属性,这个属性的值也是一个对象
        //这一套主题所有的相关颜色
        theme-color:red,
        button-primary:blue
    )
)!default;

样式的生成通过方法来生成,方法定义在src/myui/css/mixin.scss:

css 复制代码
@mixin get-backcolor($color){// 在scss中,通过@mixin定义一个方法
   // scss中使用@each进行循环,这里循环variable.scss中的$themes对象,因此$themename指default-theme,$theme为default-theme的值
   @each $themename,$theme in $themes {
      [data-skin='#{$themename}'] &{// 如果html标签上面的data-skin属性等于主题的名字,就为当前元素的background-color设置某个颜色,颜色的获取通过map-get方法获取
         // 就是通过map对象获取值,map对象就是default-theme的值,$color是这个方法传进来的变量,但实际上是default-theme的值(对象)的键,
         // 所以map-get($theme,$color)取的是:default-theme的值(对象)的$color指向的键的值
         background-color: map-get($theme,$color);
      }
   }
}
// 如果要写完整的话,还需要定义生成字体颜色的方法,但是本文用生成背景颜色的方法作为示例
// @mixin get-fontcolor(){

// }

之后属性由方法来生成,src/myui/css/index.scss中定义样式:

css 复制代码
 @import "./variable.scss";
 @import "./mixin.scss";
 .header{
  width: 100%;
  min-height: 30px;
 }
 .button{
  min-width: 40px;
 }
 .background-theme{
   @include get-backcolor('theme-color')// 在scss中使用@include调用方法
 }
 .button-primary{
   @include get-backcolor('button-primary')
 }

为根节点设置data-skin属性,在src/App.vue的script标签中添加代码:

javascript 复制代码
onMounted(() => {
  window.document.documentElement.setAttribute('data-skin','default- theme')
})

在src/myindex.scss中自定义两套皮肤:

css 复制代码
$themes:(
    skin1:(
        theme-color:var(--data-theme-color,pink),
        button-primary:var(--data-button-primary,blue),
    ),
    skin2:(
        theme-color:var(--data-theme-color,black),
        button-primary:var(--data-button-primary,green),
    )
);
@import "@/myui/css/index.scss"

在src/App.vue中添加按钮,点击后进行皮肤切换

html 复制代码
<script setup>
import HelloWorld from "./components/HelloWorld.vue";
import TheWelcome from "./components/TheWelcome.vue";
import { onMounted, ref } from "vue";
onMounted(() => {
  window.document.documentElement.setAttribute('data-skin','skin1')
})
function changeskin(name) {
  window.document.documentElement.setAttribute('data-skin',name)
 }
</script>

<template>
  <div>
      <button @click="changeskin('skin1')">皮肤1</button>
      <button @click="changeskin('skin2')">皮肤2</button>
      <myheader></myheader>
      <mybutton>1231</mybutton>
  </div>
</template>

<style scoped>
header {
  line-height: 1.5;
}

.logo {
  display: block;
  margin: 0 auto 2rem;
}

@media (min-width: 1024px) {
  header {
    display: flex;
    place-items: center;
    padding-right: calc(var(--section-gap) / 2);
  }

  .logo {
    margin: 0 2rem 0 0;
  }

  header .wrapper {
    display: flex;
    place-items: flex-start;
    flex-wrap: wrap;
  }
}
</style>

这样,我们就利用scss完成了换肤:首先借助scss的方法,修改变量的定义的形式,通过方法生成样式的属性,根据html上的属性名来决定用哪一套变量,从而生成不同的颜色。接下来配合css变量

相关推荐
凡大来啦2 分钟前
Axios发起HTTP请求时的先后执行顺序
前端·javascript·http
Jason秀啊15 分钟前
前端面试题-问答篇-5万字!
前端·面试·前端面试
傻小胖19 分钟前
react中hooks之 React 19 新 Hooks useActionState & useFormStatus用法总结
前端·react.js·前端框架
16年上任的CTO27 分钟前
一文大白话讲清楚webpack基本使用——4——vue-loader的配置和使用
前端·javascript·webpack·ecmascript·vue-loader·vueloaderplugin
16年上任的CTO27 分钟前
一文大白话讲清楚webpack基本使用——9——预加载之prefetch和preload以及webpackChunkName的使用
前端·webpack·node.js·webpack preload·prefetch
软件工程师文艺2 小时前
使用HTML5 Canvas 实现呼吸粒子球动画效果的原理
前端·javascript·html·html5
不在··2 小时前
Axios HTTP库基础教程:从安装到GET与POST请求的实现
前端·javascript·vue.js
Want5957 小时前
HTML新春烟花
前端·html
baby_hua7 小时前
AI生成文档——Uni-App CSS 样式开发指南
css·uni-app·notepad++
2401_897916848 小时前
Android 解析蓝牙广播数据
android·java·前端