🍯这么好用的scss你干嘛不用!

4,266 阅读8分钟

⛳️前言

以往制定样式一直是用的css或者less,这次接触到的项目用的scss,然后就赶紧过来学一下!

🍯css预处理器

css指层叠样式表 (Cascading Style Sheets),是一门非程序式语言,没有变量、函数、scope(作用域)等概念。

我认为css有三个最大的缺点😁

◾️ css并不存在逻辑。这点是我认为css最欠缺的地方,不过目前貌似并没有任何一个工具能解决这个问题,css的动态调整依然时js在做。

◾️ css自身无合适的变量机制,虽然有--varible的语法,但是原生css的变量机制很难用。

◾️ css无法嵌套,导致css的可读性并不高,大部分时候就是只有写代码的人看看css,接手的人很容易一脸懵。

⁉️这样的缺陷可能会导致以下问题

◾️ 冗余度高:需要书写大量看似没有逻辑的代码

◾️ 不方便维护及扩展,难以复用

◾️ css没有很好的计算能力

一般来说,非前端工程师往往会因为缺少css编写经验而很难写出组织良好且易于维护的CSS代码。

鉴于这些问题,css预处理器定义出了一种新的语言,其基本思想是,用一种专门的编程语言,进行 Web 页面样式设计,再通过编译器转化为正常的 CSS 文件,以供项目使用。它使得css更加简洁、方便修改、可读性强、适应性强并且更易于代码的维护。

css预处理器包括less,scss,sass,stylus

sass是由Ruby语言编写的一款css预处理语言,和html一样有严格的缩进风格,和css编写规范有着很大的出入,是不使用花括号和分号的,所以不被广为接受。

SCSS 和 CSS 写法无差别,这也是 Sass 后来越来越受大众喜欢原因之一。简单点说,把你现有的“.css”文件直接修改成“.scss”即可使用。Sass 从第三代开始,放弃了缩进式风格,并且完全向下兼容普通的 CSS 代码,这一代的 Sass 被称为 Scss 。

// sass
#sidebar
   width: 30%
   background-color: #faa
   
// scss
#sidebar {
   width: 30%;
   background-color: #faa;
}

简单了解之后,让我们进入正题!

🍯准备工作

scss需要经过编译为css才能被浏览器识别,我们直接使用webpack进行编译。

相关:概念 | webpack 中文文档

如果懒得自己动手配置,可以直接利用在线平台将scss编译成css👉SassMeister | The Sass Playground!

示例文件结构:

首先安装css-loader、style-loader、node-sass、sass-loader。

npm install css-loader style-loader --save-dev
npm install node-sass sass-loader --save-dev

webpack.config.js配置文件中添加对应的loader,完整的配置图如下:

const path = require("path");
const { VueLoaderPlugin } = require('vue-loader');
module.exports = {
    entry: "./src/index",
    output: {
        filename: "[name].js",
        path: path.join(__dirname, "./dist"),
    },
    module: {
        rules: [
            {
                test: /.scss/,
                use: ['style-loader', 'css-loader', 'sass-loader']
            },
            {
                test: /.vue$/,
                use: 'vue-loader'
            }
        ]
    },
    plugins: [
        new VueLoaderPlugin()
    ],
    mode: "production"
};

创建index.scss,然后在入口文件引入

// src/index.scss
$border-color: #aaa; //声明变量

.container {
    $border-width: 1px;
    border: $border-width solid $border-color; //使用变量
}

// src/index.js
import './index.scss'

后面我们将在index.scss中编写scss代码,然后运行npx webpack,等待结果的输出!

☎️注意

◾️ 使用npx:npm install -g npx

可以参考:npx使用方法

◾️ 运行后控制台可能会报错缺少vue-loader,那就npm i vue-loader,我自己后面运行的时候还报以下的错,解决方式是npm i vue@3.2.26

让我们开始吧!迫不及待迫不及待!!

🍯scss

🍻使用变量

SCSS中的变量以$开头。

$border-color: #aaa; //声明全局变量

.container {
    $border-width: 1px;//声明局部变量
    border: $border-width solid $border-color; //使用变量
}

在这个例子中,我们定义了两个变量,其中$border-color在大括号之外称为全局变量,它任何地方都可以使用,$border-width是在.container之内声明的,是局部变量,只有.container内部才能使用。

编译后的css:

.container {
    border:1px solid #aaa; //使用变量
}

SCSS相当于模板引擎,编译的过程中用变量的值去替代变量所占据的位置。

☎️注意

◾️ SCSS中变量名使用中划线或下划线都是指向同一变量的。

◾️ 后定义的变量声明会被忽略,但赋值会被执行,这一点和ES5中var声明变量是一样的。

所以,如果在上面代码的基础上,再定义一个全局变量$border_color:#ccc,那他们将会指向同一个变量,.container的值会被第二次定义的变量覆盖。

好像看过不区分的原因,但是忘记了😭

😋嘿嘿找到了!这是Sass 早期的历史遗留问题,当时它只允许在标识符名称中使用下划线。一旦 Sass 添加了对连字符的支持以匹配 CSS 的语法,这两者就等同于,使迁移更容易。

🍻嵌套规则

有些时候,我们为了更具体的定位元素,所以在样式中给子元素样式时我们可能会这么写

/*.father写了很多遍*/
.father ul {
    border: 1px solid #aaa;
    list-style: none;
}

.father ul:after {
    display: block;
    content: "";
    clear: both;
}

.father ul li {
    float: left;
}

.container ul li>a {
    display:inline-block;
    padding:6px 12px;
}

如果用scss的话,可以这么写

🍶嵌套选择器

/*scss*/
.father ul {
    border:1px solid #aaa;
    list-style:none;
    
    li {
        float:left;
    }
    
    li>a {
        display:inline-block;
        padding:6px 12px;
    }
}

.father ul:after {
    display:block;
    content:"";
    clear:both;
}

🍶嵌套中的父级选择器

scss中使用&表示选中当前元素的父元素,也就是父级选择器

.father ul {
    border:1px solid #aaa;
    list-style:none;
    
    li {
        float:left;
    }
    
    &:after {
        display:block;
        content:"";
        clear:both;
    }
}

☎️注意

◾️ 只能在嵌套内部使用父级选择器,否则SCSS找不到父级元素会直接报错。

◾️ 伪类选择器中,父级选择器十分常用

🍶嵌套组合选择器

在嵌套规则中可以写任何css代码,包括群组选择器(,),子代选择器(>),同层相邻组合选择器(+)、同层全体组合选择器(~)等等

/*scss*/
.father ul {
    border:1px solid #aaa;
    list-style:none;
    
    li {
        float:left;
        
        >a {
            display:inline-block;
            padding:6px 12px;
        }
    }
    
    &:after {
        display:block;
        content:"";
        clear:both;
    }
}

子代选择器可以写在外层选择器右边(如下述例子)也可以写在内层选择器左边(如上述例子)。

li >{ 
    a {
        display:inline-block;
        padding:6px 12px;
    }
}

☎️注意

◾️ 写在外层选择器右边时要特别注意,它会作用于所有嵌套的选择器上,所以尽量不要采用这类写法,扩展性不强,也容易出错

🍶嵌套属性

scss识别一个属性以分号结尾时则判断为一个属性,以大括号结尾时则判断为一个嵌套属性,规则是将外部的属性以及内部的属性通过中划线连接起来形成一个新的属性。

例子:

li {
    border:1px solid #aaa;
    border-left:0;
    border-right:0;
}

用scss

li {
    border:1px solid #aaa {
        left:0;
        right:0;
    }
}

🍻导入scss文件

开发一个大型项目的时候,css文件不止一个,css提供了@import命令在css内部引入另一个css文件,浏览器只有在执行到@import语句后才会去加载对应的css文件,导致页面性能变差,所以基本不使用。

SCSS中的@import命令跟原生的不太一样。

🍶导入变量的优先级问题-变量默认值(!default)

/*App1.scss*/
$border-color:#aaa; //声明变量
@import App2.scss;  //引入另一个SCSS文件
.container {
    border:1px solid $border-color; //使用变量
}
/*App2.scss*/
$border-color:#ccc; //声明变量

/*生成的css文件*/
.container {
    border:1px solid #ccc; //使用变量
}

我们可以看到,变量$border-color的值被引入的scss文件中的同名变量所污染覆盖,如果我们不想这种事情发生,我们可以使用变量默认值。

/*App1.scss*/
$border-color:#aaa; //声明变量
@import App2.scss;  //引入另一个SCSS文件
.container {
    border:1px solid $border-color; //使用变量
}
/*App2.scss*/
$border-color:#ccc !default; //声明变量

/*生成的css文件*/
.container {
    border:1px solid #aaa; //使用变量
}

使用!default后,导入的文件App2.scss只在文件中不存在bordercolor时起作用,若App1.scss中已经存在了border-color时起作用,若App1.scss中已经存在了border-color变量,则App2.scss中的$border-color不生效。

☎️注意

◾️ !default只能使用于变量中。

🍶嵌套导入-局部导入

上面的例子是全局导入App2.scss,其实我们也可以局部导入

/*App1.scss*/
$border-color:#aaa; //声明变量
.container {
    @import App2.scss;  //引入另一个SCSS文件
    border:1px solid $border-color; //使用变量
}
/*App2.scss*/
$border-color:#ccc !default; //声明变量
p {
    margin:0;
}

/*生成的css文件*/
.container {
    border:1px solid #aaa; //使用变量
}
.container p {
    margin:0;
}

结果就是将App2.scss中的所有内容直接写入到App1.scss的.container选择器中

🍶使用原生@import

某些情况下我们不得不使用原生@import时了,SCSS也为我们处理了这种情况,直接导入css文件即可

@import 'App.css';

🍻注释

SCSS中的注释有两种

◾️ /注释/:这种注释会被保留到编译后的css文件中。

◾️ //注释:这种注释不会被保留到编译后生成的css文件中。

🍻混合器@mixin(宏定义)

🍶声明一个函数

使用@mixin指令声明一个函数,提高复用率

@mixin border-radius{
    -moz-border-radius: 5px;
    -webkit-border-radius: 5px;
    border-radius: 5px;
    color:red;
}

混合器作用域内的属性都是return的值,除此之外,还可以为函数传参数。

@mixin get-border-radius($border-radius,$color){
    -moz-border-radius: $border-radius;
    -webkit-border-radius: $border-radius;
    border-radius: $border-radius;
    color:$color;
}

也可以设置混合器的默认值。

真不错

@mixin get-border-radius($border-radius:5px,$color:red){
    -moz-border-radius: $border-radius;
    -webkit-border-radius: $border-radius;
    border-radius: $border-radius;
    color:$color;
}

🍶使用函数

使用函数的关键字为@include,多个参数时,传参指定参数的名字,可以不用考虑传入的顺序。

.container {
    border:1px solid #aaa;
    @include get-border-radius;         //不传参则为默认值5px
    @include get-border-radius(10px,blue);   //传参
}

/*多个参数时,传参指定参数的名字,可以不用考虑传入的顺序*/
.container {
    border:1px solid #aaa;
    @include get-border-radius;         //不传参则为默认值5px
    @include get-border-radius($color:blue,$border-radius:10px);   //传参
}

☎️注意

◾️ 混合器中可以写一切scss代码。

🍻@function

没错我就是看了这个,所以才过来看scss

函数是使用@function规则定义的,该规则编写为@function <name> (<arguments...>){...}。函数的名称可以是任何 Sass 标识符。它只能包含通用语句,以及@return规则,该规则指示要用作函数调用结果的值。使用常规 CSS 函数语法调用函数。

/*scss语法*/
@function pow($base, $exponent) {
  $result: 1;
  @for $_ from 1 through $exponent {
    $result: $result * $base;
  }
  @return $result;
}

.sidebar {
  float: left;
  margin-left: pow(4, 3) * 1px;
}

/*css语法*/
.sidebar {
  float: left;
  margin-left: 64px;
}

☎️注意

◾️ 虽然从技术上讲,函数可能会产生诸如设置全局变量之类的副作用,但强烈建议不要这样做。

◾️ 使用mixins 来产生副作用,并使用函数来计算值。

🍶参数

参数,允许在每次调用函数时。自定义函数的行为。参数在@function函数名称之后的规则中,指定为用括号括起来的变量名称列表。该函数必须以 SassScript表达式的形式。使用相同数量的参数调用。这些表达式的值,在函数体中作为相应的变量可用。

参数列表也可以有尾随逗号!这使得重构样式表时更容易避免语法错误。

🔸可选参数

可以通过定义一个默认值来使参数成为可选参数,如果该参数未传递,则将使用该默认值。默认值使用与变量声明相同的语法

/*scss*/
@function invert($color, $amount: 100%) {
  $inverse: change-color($color, $hue: hue($color) + 180);
  @return mix($inverse, $color, $amount);
}

$primary-color: #036;
.header {
  background-color: invert($primary-color, 80%);
}

/*css*/
.header {
  background-color: #523314;
}

🔸关键字参数

可以按名称传递参数。这对于具有多个可选参数的函数或带有布尔参数的函数特别有用,这些参数的含义在没有名称的情况下并不明显。关键字参数使用与变量声明和可选参数相同的语法。

@function scale-color($primary-color, $lightness){...}
$primary-color: #036;
.banner {
  background-color: $primary-color;
  color: scale-color($primary-color, $lightness: +40%);
}

.banner {
  background-color: #036;
  color: #0a85ff;
}

🔸接收任意参数

如果@function声明中的最后一个参数以结尾 ... ,则该函数的所有额外参数都作为列表传递给该参数。此参数称为参数列表。

/*scss*/
@function sum($numbers...) {
  $sum: 0;
  @each $number in $numbers {
    $sum: $sum + $number;
  }
  @return $sum;
}

.micro {
  width: sum(50px, 30px, 100px);
}

/*css*/
.micro {
  width: 180px;
}

🔸接收任意关键字参数

meta.keywords()函数接受一个参数列表并返回任何额外的关键字,这些关键字作为从参数名称(不包括$)到这些参数值的映射传递给函数。如果我们从未将参数列表传递给meta.keywords()函数,则该参数列表将不允许额外的关键字参数,这个可以防止意外拼错任何参数名称。

🔸传递任意参数

就像参数列表允许函数接受任意位置或关键字参数一样,我们可以使用相同的语法将位置参数和关键字参数传递给函数。

/*scss*/
$widths: 50px, 30px, 100px;
.micro {
  width: min($widths...);
}

/*css*/
.micro {
  width: 30px;
}

而且因为参数列表同时跟踪位置参数和关键字参数,所以我们可以使用它同时将两者传递给另一个函数。这使得为函数定义别名变得非常容易!

@function fg($args...) {
  @warn "The fg() function is deprecated. Call foreground() instead.";
  @return foreground($args...);
}

🍶@return

@return规则指示用作调用函数结果的值。

它只允许在@function正文中,并且每个@function必须以@return结尾。当遇到@return时立即结束函数并返回其结果。

@use "sass:string";

@function str-insert($string, $insert, $index) {
  // Avoid making new strings if we don't need to.
  @if string.length($string) == 0 {
    @return $insert;
  }

  $before: string.slice($string, 0, $index);
  $after: string.slice($string, $index);
 @return $before + $insert + $after;
}

🍻继承@extend

搞什么?降低代码量是吧,我直接面向对象!

🍶定义被继承的样式

使用%定义一个被继承的样式,类似静态语言中的抽象类,本身不起作用,只用于被其他人继承。

%border-style {
  border:1px solid #aaa;
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
  border-radius: 5px;
}

🍶继承样式

通过关键字@extend即可完成继承。

.container {
        @extend %border-style;
}

什么?上述例子中看不出混合器与继承之间的区别,那你可看这个!

.container {
        @extend %border-style;
        color:red;
}
.container1 {   //继承另一个选择器
        @extend .container;
}

🍻操作符

SCSS提供了标准的算术运算符,例如+、-、*、/、%

/*SCSS*/
width: 600px / 960px * 100%;

/*编译后的CSS*/
width: 62.5%;

🍻跳出根元素@at-root

@at-root 从字面上解释就是跳出根元素。当你选择器嵌套多层之后,想让某个选择器跳出,此时就可以使用 @at-root

/*scss*/
.wrapper {
    width:100%;
    height:100%;
    @at-root .active{
        color:red
    }
}

/*css*/
.wrapper {
    width:100%;
    height:100%;
}
.active{
    color:red
}

🍻@content

@content 用在 mixin 里面的,当定义一个 mixin 后,并且设置了 @content@include 的时候可以传入相应的内容到 mixin 里面

🍻map-get

map-get(map,key) 函数的作用是根据 key 参数,返回 key 在 map 中对应的 value 值。如果 key 不存在 map 中,将返回 null 值。此函数包括两个参数:

map:定义好的 map。 key:需要遍历的 key。

假设要获取 facebook 键值对应的值 #3b5998,我们就可以使用 map-get() 函数来实现:

🍻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

🍻循环

🍶@for

@for 指令包含两种格式:@for $var from <start> through <end>或者 @for $var from <start> to <end>,区别在于 through 与 to 的含义:当使用 through 时,条件范围包含startend的值,而使用 to 时条件范围只包含start的值不包含end的值。另外,$var 可以是任何变量,比如 $i;<start><end>必须是整数值。

不用@for时候

.wes { 
    /* 多出部分用省略号表示 , 用于一行 */
    overflow:hidden;
    word-wrap:normal;
    white-space:nowrap;
    text-overflow:ellipsis;
}
.wes-2 { 
    /* 适用于webkit内核和移动端 */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
} 
.wes-3 {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;
}
.wes-4 {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 4;
    overflow: hidden;
}

用上@for

太牛了!

@for $i from 1 through 4 {
  .wes-#{$i} {
    overflow: hidden;
    @if ($i==1) {
      word-wrap: normal;
      white-space: nowrap;
      text-overflow: ellipsis;
    } @else {
      display: -webkit-box;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: $i;
    }
  }
}

🍶@each 循环

🍻对象操作(key:value)

原始版

h1 {
  font-size: 2em; }
h2 {
  font-size: 1.5em; }
h3 {
  font-size: 1.2em; }

用上对象操作

@each $header, $size in (h1: 2em, h2: 1.5em, h3: 1.2em) {
  #{$header} {
    font-size: $size;
  }
}

🍯less和scss的区别

了解了一圈scss后,我们也被忘记了less,让我们看看他俩的区别,gogogo!

🍻相同点

🔸 less和scss都是css的预处理器,可以拥有变量,运算,继承,嵌套的功能,使用两者可以使代码更加的便于阅读和维护。

🔸 都可以通过自带的插件,转成相对应的css文件。

🔸 都可以参数混入,可以传递参数的class,就像函数一样

🔸 嵌套的规则相同,都是class嵌套class

🍻不同点

🔸 声明和使用变量:less用@符号,scss用$符号表示

🔸 变量插值:less采用@{XXXX}的形式,scss采用${XXXX}的形式

可以用为less和scss声明变量,变量作为css的选择器

 @main-top : search;
 .@{ main-top } {
     font-size24px;
     color : #fff;
 }
 // 是用LESS,定义类,类选择器选中search标签,给其设置css样式
 
 $main-top : search;
.${ main-top } {
        font-size24pxcolor : #fff;
}
// 是用SCSS,定义类,类选择器选中search标签,给其设置css样式

🔸 scss支持条件语句(if、else、for循环等),less不支持

 
 /* Sample Sass “if” statement */
  
 @if lightness($color) > 30% {
  
 } @else {
  
 }
  
 /* Sample Sass “for” loop */
  
 @for $i from 1 to 10 {
   .border-#{$i} {
     border: #{$i}px solid blue;
   }
 

🔸 应用外部css文件方式不同

SCSS应用的css文件名必须以‘_’开头(下划线),文件名如果以下划线开头的话,sass会认为该文件是一个应用文件,不会将它转成css文件

 // 源代码:
 @import "_main.css";
 @import "_layout.css";
 @import "_text.css";

🔸 颜色函数

调整色相的话,LESS使用spin()的函数;SCSS使用名为adjust_hue()的函数

/*less*/
 @state-success-border: darken(spin(@state-success-bg, -10), 5%);
 
 /*scss*/
  $state-success-border: darken(adjust_hue($state-success-bg, -10), 5%);

🔸引用父选择器&符号的使用

LESS和SCSS都可以使用&符号表示父选择器,但是SCSS的&符号只能出现在一个组合选择器的开始位置,LESS则没有这个限制

/*less*/
 .bg-variant(@color) {
   background-color: @color;
   a&:hover,
   a&:focus {
     background-color: darken(@color, 10%);
   }
 }
 
 /*scss*/
 a {
   font-weight: bold;
   text-decoration: none;
   &:hover { text-decoration: underline; }
   body.firefox & { font-weight: normal; }
 }

🍯如何利用scss一键换肤?

很多时候我们需要一键换肤的功能,刚接触前端时候我们是直接另外建立一个css文件,通过更换css文件来达到这个效果。但是这样其实会造成很多的代码冗余,如果用scss的写法,利用scss的优点提前定义一张套css环境变量体系会大大优化代码!

以下将会用一个demo来实现一键换肤的效果,那我们开始吧

🍻项目的目录结构

src
├── App.vue
├── main.js
├── router
│   └── index.js
├── store
│   └── index.js
├── style
│   ├── settings
│   │   └── variable.scss  // 样式变量定义文件
│   └── theme
│       ├── default
│       ├── index.scss // 主题入口文件
│       └── old
└── views
    ├── Home.vue // 主题切换页面
    ├── List.vue
    └── Mine.vue

🍻环境准备

🔸npm i sass

🔸npm i -D sass-loader @10.1.0

注意 sass-loader 安装需要指定版本 如果安装最新版本会报错 this.getOptions 这个方法未定义

🔸npm i -S normalize.css

利用 normalize.css 初始化页面样式*

🍻定义变量

提前把一些常用的主题色,字体大小,以及边距这种与视觉沟通好,然后定义对应的变量。

🍻定义主题

🍶默认主题:./src/theme/default/index.scss

/* 默认主题色 */
@import "../../settings/variable.scss";
$theme-default: (
  color: $info,
  font-weight: $font-weight-bold,
  font-size: $font-size-lg,
);

🍶老年主题:./src/theme/old/index.scss

/* 老年主题色 */
@import "../../settings/variable.scss";
$theme-old: (
  color: $danger,
  font-weight: $font-weight-bold,
  font-size: $font-size-slg,
);

🍶入口文件:./src/theme/index.scss

定义混合指令,切换主题,并将主题中的所有规则添加到 theme-map 中

@import "../settings/variable.scss";

@import "./default/index.scss";
@import "./old/index.scss";

$themes: (
  default: $theme-default,
  old: $theme-old,
  /*...其他主题*/
);

$theme-map: null;

// 第三步: 定义混合指令, 切换主题,并将主题中的所有规则添加到theme-map中
@mixin themify() {
  @each $theme-name, $map in $themes {
    // & 表示父级元素  !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);
}

🍶 vue.config.js 配置项处理

🔸在配置项中利用 CSS 插件自动注入全局变量样式

🔸利用 webpack 自定义插件遍历主题目录文件,自动生成自定义主题目录数组

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);
  }
});

//利用 CSS 插件自动注入全局变量样式
module.exports = {
  css: {
    loaderOptions: {
      scss: {
        // 注意: 在sass-loader v8 中,这个选项是prependData
        additionalData: `@import "./src/style/theme/index.scss";`,
      },
    },
  },
  configureWebpack: (config) => {
    return {
      plugins: [
        new webpack.DefinePlugin({
          THEMEARR: JSON.stringify(ThemesArr),
          THEMEFILES: JSON.stringify(themeFiles),
        }),
      ],
    };
  },
};

🍻主题色切换

🍶在 App.vue 文件下的 mounted 中将 body 添加一个自定义的 data-theme 属性,并将其设置为 default

🍶切换 js 逻辑实现

🔸初始化页面时,获取到默认主题

// Home.vue
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.showPicker = false;
    this.currentThemeIndex = this.themeValue.findIndex(
      (theme) => theme === currentTheme
    );
    document
      .getElementsByTagName("body")[0]
      .setAttribute("data-theme", THEMEARR[this.currentThemeIndex]);
  },
}

🔸综合使用

//Home.vue

<script>
export default {
  name: "Home",
  components: {},
  data() {
    return {
      currentTheme: "default",
      currentThemeIndex: 0,
      // themeCName: ["默认", "老人"],
      themeValue: [],
      gridIcon: ["location-o", "like-o"],
      gridNum: [8, 4],
      columnNum: [4, 2],
      showPicker: false,
    };
  },
  methods: {
    onConfirm(currentTheme) {
      this.currentTheme = currentTheme;
      this.showPicker = false;
      this.currentThemeIndex = this.themeValue.findIndex(
        (theme) => theme === currentTheme
      );
      document
        .getElementsByTagName("body")[0]
        .setAttribute("data-theme", THEMEARR[this.currentThemeIndex]);
    },
  },
  mounted() {
    this.themeValue = THEMEARR;
    this.currentThemeIndex = this.themeValue.findIndex(
      (theme) => theme === "default"
    );
    this.currentTheme = this.themeValue[this.currentThemeIndex];
  },
};
</script>

<style lang="scss">
.home {
  padding: 10px;

  .t-home-title,
  .t-home-sub-title,
  .t-home-info {
    @include themify() {
      color: themed("color");
      font-weight: themed("font-weight");
      font-size: themed("font-size");
    }
  }
}
</style>
<script>
export default {
  name: "Home",
  components: {},
  data() {
    return {
      currentTheme: "default",
      currentThemeIndex: 0,
      // themeCName: ["默认", "老人"],
      themeValue: [],
      gridIcon: ["location-o", "like-o"],
      gridNum: [8, 4],
      columnNum: [4, 2],
      showPicker: false,
    };
  },
  methods: {
    onConfirm(currentTheme) {
      this.currentTheme = currentTheme;
      this.showPicker = false;
      this.currentThemeIndex = this.themeValue.findIndex(
        (theme) => theme === currentTheme
      );
      document
        .getElementsByTagName("body")[0]
        .setAttribute("data-theme", THEMEARR[this.currentThemeIndex]);
    },
  },
  mounted() {
    this.themeValue = THEMEARR;
    this.currentThemeIndex = this.themeValue.findIndex(
      (theme) => theme === "default"
    );
    this.currentTheme = this.themeValue[this.currentThemeIndex];
  },
};
</script>

<style lang="scss">
.home {
  padding: 10px;

  .t-home-title,
  .t-home-sub-title,
  .t-home-info {
    @include themify() {
      color: themed("color");
      font-weight: themed("font-weight");
      font-size: themed("font-size");
    }
  }
}
</style>

🍠效果图

到这里scss就入门完毕啦!!接下来就看大家了嘻嘻嘻!

🍯推荐阅读

🔸 如何利用 SCSS 实现一键换肤

🔸 听说你还不会SCSS?带你掌握scss所有知识点(上):Sass的介绍、安装和文件编译