【sass+css变量实现换肤】
文章目录
- 需求
- 思路
- 实现
- 组件
- 手动换肤
- 动态换肤
需求
假如你最近在开发一套组件库,如何让用户可以换肤呢?
- 使用者可以固定自定义主题色、基本色等相关主题
- 也支持动态切换,可以只切换单个颜色,也支持全部切换
思路
定义sass或者css变量,所有的颜色都用变量定义,当用户自定义颜色时,通过覆盖或者修改变量完成换肤
组件库css样式设计注意点:
- 用bem方式命名:块、元素、修饰符
- 结构和皮肤分离
实现
组件
这里拿两个组件举例子,一个是src/myui/mybutton.vue:
<template>
<button class="button button-primary">
<slot></slot>
</button>
</template>
<script>
export default {
name: "mybutton",
};
</script>
另一个是src/myui/myheader.vue:
<template>
<!-- 定义结构和皮肤的类分离开 -->
<header class="header background-theme">12312</header>
</template>
<script>
export default {
name: "myheader",
};
</script>
手动换肤
定义默认的颜色变量文件src/myui/css/variable.scss:
$theme-color:red !default;// 默认皮肤都要加default,如果不加default,后面可能无法覆盖这个变量
$button-primary:blue !default;
在main.js中引入默认样式:
import { createApp } from 'vue'
import App from './App.vue'
import myui from "./myui"
import "./variable.scss"
createApp(App).use(myui).mount('#app')
使用:
<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中定义样式:
@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:
// 编写变量来覆盖变量
$theme-color:pink;
$button-primary:yellow;
@import "@/myui/css/index.scss"// 引入组件库的样式文件,一定要写在最后
然后改变main.js中的引用:
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的内容改为:
// $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:
@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中定义样式:
@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标签中添加代码:
onMounted(() => {
window.document.documentElement.setAttribute('data-skin','default- theme')
})
在src/myindex.scss中自定义两套皮肤:
$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中添加按钮,点击后进行皮肤切换
<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变量