环境准备
首先我们需要安装 scss 解析环境
npm i sass
// 注意 sass-loader 安装需要指定版本 如果安装最新版本会报错 this.getOptions 这个方法未定义
npm i -D sass-loader@10.1.0
// 利用 normalize.css 初始化页面样式
npm i -S normalize.css
小技巧
这里讲一个小技巧,定义的时候可以先定义一个基准变量 base-param 然后其他状态的值可以依赖这个 base-param 进行缩放或放大实现。比如不同大小规模的字体可以采用这种方法。
// 行高
$line-height-base: 1.5 !default;
$line-height-lg: 2 !default;
$line-height-sm: 1.25 !default;
// ./style/settings/variable.scss
// 字体颜色
$info: #17a2b8 !default;
$danger: #dc3545 !default;
// 字体大小 浏览器默认 16px
$font-size-base: 1rem !default;
$font-size-lg: $font-size-base * 1.25 !default;
$font-size-slg: $font-size-base * 1.75 !default;
// 字重
$font-weight-normal: 400 !default;
$font-weight-bold: 600 !default;
定义一个入口文件
// ./style/theme/index.scss
@import "../settings/variable.scss";
$themes-color: (
default: (
// 全局样式属性
color: $info,
font-weight: $font-weight-normal,
font-size: $font-size-lg,
),
old: (
color: $danger,
font-weight: $font-weight-bold,
font-size: $font-size-slg,
),
// ... 可自定义其他主题
);
//注意需要在./style/theme下创建对应的目录default、old
//用户vue.config.js获取目录名
vue.config.js 配置项处理
我们不想每次都引入 CSS 变量,可以在配置项中利用 CSS 插件自动注入全局变量样式。
// vue.config.js
module.exports = {
css: {
loaderOptions: {
scss: {
// 注意: 在 sass-loader v8 中,这个选项是 prependData
additionalData: `@import "@/style/theme/index.scss";`,
},
},
},
};
主题色切换
主题色定义好之后就需要对他进行切换了。这也是一键换肤最核心的逻辑。
- 在 App.vue 文件下的 mounted 中将 body 添加一个自定义的 data-theme 属性,并将其设置为 default
// App.vue mounted() { document .getElementsByTagName("body")[0]
.setAttribute("data-theme", "default"); },
- 利用 webpack 自定义插件遍历主题目录文件,自动生成自定义主题目录数组
// vue.config.js
const fs = require("fs");
const webpack = require("webpack");
// 获取主题文件名
const themeFiles = fs.readdirSync("./src/style/theme");
let ThemesArr = [];
themeFiles.forEach(function (item, index) {
let stat = fs.lstatSync("./src/style/theme/" + item);
if (stat.isDirectory() === true) {
ThemesArr.push(item);
}
});
module.exports = {
css: {...},
configureWebpack: (config) => {
return {
plugins: [
// 自定义webpack插件
new webpack.DefinePlugin({
THEMEARR: JSON.stringify(ThemesArr),
}),
],
};
},
};
- 切换 js 逻辑实现
初始化页面的时候,获取到默认主题
// Home.vue
//template
<div @click="onConfirm('old')">变old</div>
<div @click="onConfirm('default')">变default</div>
//script
data(){
return {
themeValue:'',
list:[''],
currentTheme:'',
currentThemeIndex:0
}
},
mounted() {
this.themeValue = THEMEARR;
this.currentThemeIndex = this.themeValue.findIndex(
(theme) => theme === "default"
);
this.currentTheme = this.themeValue[this.currentThemeIndex];
},
把选择的主题赋值给自定义属性 data-theme
// Home.vue
// 核心切换逻辑
methods: {
onConfirm(currentTheme) {
this.currentTheme = currentTheme;
this.currentThemeIndex = this.themeValue.findIndex(
(theme) => theme === currentTheme
);
document
.getElementsByTagName("body")[0]
.setAttribute("data-theme", THEMEARR[this.currentThemeIndex]);
},
}
根vue文件下设置全局的样式
//app.vue
@import "./style/theme/index.scss";
@mixin themify() {
@each $theme-name, $map in $themes-color {
// & 表示父级元素 !global 表示覆盖原来的
[data-theme="#{$theme-name}"] & {
$theme-map: () !global;
// 循环合并键值对
@each $key, $value in $map {
$theme-map: map-merge(
$theme-map,
(
$key: $value,
)
) !global;
}
// 表示包含 下面函数 themed()
@content;
}
}
}
@function themed($key) {
@return map-get($theme-map, $key);
}
#app{
@include themify() {
color: themed("color");
font-weight: themed("font-weight");
font-size: themed("font-size");
}
}
学习其中sass的知识
-
使用 @each 循环
1.循环一个 list: 类名为 icon-10px 、icon-12px、icon-14px 写他们的字体大小写法就可以如下:
2、循环一个 map:类名为 icon-primary、icon-success、icon-secondary 等,但是他们的值又都是变量,写法如下:
-
map-get
map-get(map,key) 函数的作用是根据 key 参数,返回 key 在 map 中对应的 value 值。如果 key 不存在 map 中,将返回 null 值。此函数包括两个参数:
map:定义好的 map。key:需要遍历的 key。
假设要获取 facebook 键值对应的值 #3b5998,我们就可以使用 map-get() 函数来实现:
- 使用&嵌套覆盖原有样式
当一个元素的样式在另一个容器中有其他指定的样式时,可以使用嵌套选择器让他们保持在同一个地方。.no-opacity &相当于.no-opacity .foo。
- map-merge
合并两个 map 形成一个新的 map 类型,即将 map2 添加到 map1的尾部
$font-sizes: ("small": 12px, "normal": 18px, "large": 24px)
$font-sizes2: ("x-large": 30px, "xx-large": 36px)
map-merge($font-sizes, $font-sizes2)
结果: "small": 12px, "normal": 18px, "large": 24px,
"x-large": 30px, "xx-large": 36px
- @content
@content 用在 mixin 里面的,当定义一个 mixin 后,并且设置了 @content;@include 的时候可以传入相应的内容到 mixin 里面