css预处理器

1,024 阅读6分钟

概念

  • 基于css的另一种语言
  • 通过工具编译成css
  • 添加了很多css不具备的特性
  • 能提升css文件的组织

常见的预处理器

  • less

基于Node写的,编译速度较快,有浏览器中直接可以使用的版本不需要预先编译

  • sass(scss)

基于ruby写的,编译速度较慢

提供的功能

  1. 嵌套: 反映层级和约束(将一类代码放到一块折叠起来,方便观看和管理)
  2. 变量和计算:减少重复代码
  3. Extend和Mixin: 代码片段(类似JS中的函数,可以重复使用)
  4. 循环: 适用于复杂有规律的样式(如:栅格中每一列都要有一个样式,就要写12个样式 ,用循环写就方便)
  5. import: CSS文件模块化(利于维护)

安装和基本使用

less

安装

  1. 安装less的预编译工具(使用node写的,通过npm发布,电脑提前安装好了node和npm)
  2. 安装less的编译包npm install less -D(加-g就安装到全局,否则就安装到项目目录下)
  3. 调用
    • 如果是项目目录下安装: ./node_modules/.bin/lessc + 其他参数
    • 如果是全局安装: lessc + 其他参数

注意:安装less后,有可能会报错无法将“lessc”项识别为 cmdlet、函数、脚本文件或可运行程序的出现这一异常问题是可能是由于系统默认开启了禁止运行脚本文件。

在开始菜单中搜索powershell命令行工具,以管理员身份运行,执行如下命令,选择Y。

set-ExecutionPolicy RemoteSigned

执行完成后再次执行

lessc -v

嵌套

批注:

  • 嵌套层级相当于父子关系,如.wrapper .nav{}
  • 同一层级,相当于兄弟关系如.wrapper .nav{},.wrapper .content{}
  • '&'符号相当于.content:hover{}
.wrappper{
    background: white;
    .nav{
        font-size: 16px;
    }
    .content{
        font-size: 14px;
        &:hover{
            background: skyblue;
        }
    }
}

编译

编译成css

less文件的后缀是.less

lessc demo.less demo.css

.wrappper {
  background: white;
}
.wrappper .nav {
  font-size: 16px;
}
.wrappper .content {
  font-size: 14px;
}
.wrappper .content:hover {
  background: skyblue;
}
 

sass

安装

如果发现安装的慢可以使用cnpm来安装

npm install -g sass

sass文件的后缀名是.scss

sass中的嵌套语法同less是一样的

编译

sass demo.scss demo2.css

编译出的css文件同less也是一样

变量

变量的存在是为了解决反复的去写相同的值,同时也意味着可以使用它们去参与一些运算

less

在less中,变量前使用符号@

@fontSize: 12px;
@bgColor: red;

注意:

  • 加法的情况下,值要带单位(乘法不用带单位),运算的时候也是带单位进行计算,如:
font-size: @fontSize + 2px;

  • 颜色也可以进行运算,less为我们提供了颜色的运算,如
/*颜色变浅*/
background: lighten(@bgColor,40%);

当我们遇到需求变更的时候,如修改主题色,只需要修改@bgColor的颜色重新编译即可,方便对样式进行修改和维护

注: 使用变量的时候,只有当彼此之间存在某种关联的时候才使用同一个变量

sass

和less不同,less中变量前使用@符号,而sass中使用$符号

$fontSize:12px;
$bgColor: red;

为什么less中变量使用@而scss中使用$?

  • less认为尽量去接近css的语法,让大家看起来熟悉,css中会使用@符号;

  • sass认为它和css是不一样的东西,应该尽量的避开以免产生混淆,所以sass使用$符号作为变量

总结:css预处理器中的变量拥有一处定义多处使用、方便维护的特性(个人更倾向于使用sass)

mixin

背景

如果想要有一段代码想要复用该怎么办?css中并没有为我们提供这样的方法,你可能会说,使用公共的样式不可以吗? =>这种方法更多的是体现在html层面中的复用,在标签中添加复用的class,css本身并没有为我们提供复用的代码的功能。如果是在css中就可以复用,那么html中的标签中就不需要添加很多引用的样式了,mixin正是为了这种场景而生的

less

.block(@fontSize){
    font-size: @fontSize;
    border: 1px solid #ccc;
    border-radius: 4px;
}

它可以接受参数,如果不想接受参数可以不写"()"

.block{
    font-size: @fontSize;
    border: 1px solid #ccc;
    border-radius: 4px;
}

在使用的地方.block()

编译前

@fontSize: 12px;
@bgColor: red;
.block(@fontSize){
    font-size: @fontSize;
    border: 1px solid #ccc;
    border-radius: 4px;
}

body{
    padding: 0;
    margin: 0;
}
.wrappper{
    background: lighten(@bgColor,40%);
    .nav{
        .block(@fontSize)
    }
    .content{
        .block(@fontSize + 2px);
        &:hover{
            background: skyblue;
        }
    }
}

编译后:

body {
  padding: 0;
  margin: 0;
}
.wrappper {
  background: #ffcccc;
}
.wrappper .nav {
  font-size: 12px;
  border: 1px solid #ccc;
  border-radius: 4px;
}
.wrappper .content {
  font-size: 14px;                 // 参数加了2px,所以是14px
  border: 1px solid #ccc;
  border-radius: 4px;
}
.wrappper .content:hover {
  background: skyblue;
}

sass

声明的时候需要显示的标记出mixin,调用的时候需要显示的标记出include

/*1. 定义  */
@mixin block($fontSize){
    font-size: $fontSize;
}

/*2. 调用  */

@include block($fontSize)

编译前:

$fontSize:12px;
$bgColor: red;
@mixin block($fontSize){
    font-size: $fontSize;
    border: 1px solid #ccc;
    border-radius: 4px;
}
body{
    padding: 0;
    margin: 0;
}
.wrappper{
    background: lighten($bgColor,40%);
    .nav{
        @include block($fontSize)
    }
    .content{
        @include block($fontSize + 2px);
        &:hover{
            background: skyblue;
        }
    }
}

编译后:

body {
  padding: 0;
  margin: 0;
}

.wrappper {
  background: #ffcccc;
}
.wrappper .nav {
  font-size: 12px;
  border: 1px solid #ccc;
  border-radius: 4px;
}
.wrappper .content {
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
}
.wrappper .content:hover {
  background: skyblue;
}

应用: 比如给清除浮动的样式声明一个mixin,在需要的地方直接调用即可;或者给绝对居中的样式声明一个mixin;或者使用css原子类的思想,指定很多独立通用的样式,这一点和masterGo原型中组件样式有点像,定义好共用的颜色、字体大小、行高、间距等

extend

背景

使用minxin编译后的代码中会有非常多重复的代码

image.png

按照一般的逻辑是在css中提取重复的样式到单独的class中,自己独有的样式再单独写,这样可以减少重复代码,减少css体积

当我们在更多的地方引用mixin的时候就会有更多重复的代码,这时候该怎么办? => extend就是为解决这个问题而生的

less

编译前

@fontSize: 12px;
@bgColor: red;
.block{
    font-size: @fontSize;
    border: 1px solid #ccc;
    border-radius: 4px;
}

body{
    padding: 0;
    margin: 0;
}
.wrappper{
    background: lighten(@bgColor,40%);
    .nav:extend(.block){
        color: #333                   // 自己独有的样式写到里面
    };
    .content{
        &:extend(.block);
        &:hover{
            background: skyblue;
        }
    }
}

编译后

.block,
.wrappper .nav,
.wrappper .content {
  font-size: 12px;
  border: 1px solid #ccc;
  border-radius: 4px;
}
body {
  padding: 0;
  margin: 0;
}
.wrappper {
  background: #ffcccc;
}
.wrappper .nav {
  color: #333;
}
.wrappper .content:hover {
  background: skyblue;
}

从上面可以看出公用的样式被提取到最上面了,.nav.content自己独有的样式被提取出来

什么时候用minxin,什么时候用extend?

  • mixin是直接把代码复制过来,适合处理更复杂的情况,如:需要一个参数去控制或者带条件
  • extend是把代码提取出来,把公共的样式写到一起,产生更小的代码

sass

区别在于,sass在调用的时候用@entend + 公用的样式

编译前:

$fontSize:12px;
$bgColor: red;
.block{
    font-size: $fontSize;
    border: 1px solid #ccc;
    border-radius: 4px;
}
body{
    padding: 0;
    margin: 0;
}
.wrappper{
    background: lighten($bgColor,40%);
    .nav{
        @extend .block;
        color: #333;
    }
    .content{
        @extend .block;
        &:hover{
            background: skyblue;
        }
    }
}

编译后:

.block, .wrappper .content, .wrappper .nav {
  font-size: 12px;
  border: 1px solid #ccc;
  border-radius: 4px;
}

body {
  padding: 0;
  margin: 0;
}

.wrappper {
  background: #ffcccc;
}
.wrappper .nav {
  color: #333;
}
.wrappper .content:hover {
  background: skyblue;
}

总结:

  • extend为我们解决了mixin代码复用导致的代码重复的问题,使我们更好的完成对样式的复用,减少代码体积,提高可维护性

  • mixin和extend增加了可维护性,例如不想要.block而是想要.card,如果是不用预处理器就要到html中找到这个class名,把所有的 block class 改为 card class。如果是在预处理器中使用minxin或者extend只需要把这个class名改下就可以了

loop

less

概念

它可以按照一定的规则生成一系列的css代码,例如栅格系统中随着列数的增加他们的宽度是不同的,这样12列就需要写12个样式,类似这种高度有规律的样式就可以使用预处理器的循环来学

less中本身是没有循环的,这就需要使用mixin借助递归

编译前:

.gen-col(@n) when (@n > 0){
    .col-@{n}{
        width: (1000px / ( 12px / @n ));    // 生成的样式
    }
    .gen-col(@n - 1);
}

.gen-col(12);

编译后

.col-12 {
  width: 1000px;
}
.col-11 {
  width: 916.66666667px;
}
.col-10 {
  width: 833.33333333px;
}
.col-9 {
  width: 750px;
}
.col-8 {
  width: 666.66666667px;
}
.col-7 {
  width: 583.33333333px;
}
.col-6 {
  width: 500px;
}
.col-5 {
  width: 416.66666667px;
}
.col-4 {
  width: 333.33333333px;
}
.col-3 {
  width: 250px;
}
.col-2 {
  width: 166.66666667px;
}
.col-1 {
  width: 83.33333333px;
}

**批注

用Less除法运算时失效不计算无法没有输出正确结果

解决方式:在除法运算斜杠前加点./ 或者加上小括号

sass

1. 使用递归

编译前

@mixin gen-col($n){
    @if $n > 0{
        .col-#{$n}{
            width:1000px / ( 12 / $n );  // 生成的样式
        } 
        @include gen-col($n - 1)
    }
   
}

@include gen-col(12);

批注:

除法运算时,如果两个值带有相同的单位值时,除法运算之后会得到一个不带单位的数值

编译后

.col-12 {
  width: 1000px;
}

.col-11 {
  width: 916.6666666667px;
}

.col-10 {
  width: 833.3333333333px;
}

.col-9 {
  width: 750px;
}

.col-8 {
  width: 666.6666666667px;
}

.col-7 {
  width: 583.3333333333px;
}

.col-6 {
  width: 500px;
}

.col-5 {
  width: 416.6666666667px;
}

.col-4 {
  width: 333.3333333333px;
}

.col-3 {
  width: 250px;
}

.col-2 {
  width: 166.6666666667px;
}

.col-1 {
  width: 83.3333333333px;
}

批注:”/ ”符号被当作除法运算符时有以下几种情况

如果数值或它的任意部分是存储在一个变量中或是函数的返回值。
如果数值被圆括号包围。
如果数值是另一个数学表达式的一部分。

2. 使用循环

编译前:

@for $i from 1 through 12 {
    .col-#{$i}{
        width: 1000px / 12 * $i;
    }
} 

编译后:

.col-1 {
  width: 83.3333333333px;
}

.col-2 {
  width: 166.6666666667px;
}

.col-3 {
  width: 250px;
}

.col-4 {
  width: 333.3333333333px;
}

.col-5 {
  width: 416.6666666667px;
}

.col-6 {
  width: 500px;
}

.col-7 {
  width: 583.3333333333px;
}

.col-8 {
  width: 666.6666666667px;
}

.col-9 {
  width: 750px;
}

.col-10 {
  width: 833.3333333333px;
}

.col-11 {
  width: 916.6666666667px;
}

.col-12 {
  width: 1000px;
}

/*# sourceMappingURL=loop2.css.map */

总结:

只要是有规律的样式都可以使用sass中的循环去生成样式

import

背景

css中的使用@import 对样式进行模块化管理存在的问题:它不会做任何的合并或者在加载的时候去复用链接,只能一个个加载,这样会增加http的连接数,造成性能上的问题,如果代码拆分的很细,这种方法是存在问题的

css预处理器在此基础上做了改进,一样可以通过import把样式引进来,但是编译的时候把所有的样式都编到一起了,最后产生的是一个css文件,这样就解决了文件太细碎加载的问题

less

编译 1-import-main.less文件前:

/*1-import-main.less*/
@import "./1-import-variable.less";  
@import "./1-import-module1.less";
@import "./1-import-module2.less";

/*1-import-variable.less*/
@themeColor: blue; 
@fontSize: 14px;

/*1-import-module1.less*/
.module1{
    .box{
        font-size: @fontSize + 2px;
        color: @themeColor;
    }
    .tips{
        font-size: @fontSize;
        color: lighten(@themeColor,40%)
    }
}   

 /*1-import-module2.less*/
.module2{
    .box{
        font-size: @fontSize + 4px;
        color: @themeColor;
    }
    .tips{
        font-size: @fontSize + 2px;
        color: lighten(@themeColor,20%)
    }
}    

编译 1-import-main.less文件后:

.module1 .box {
  font-size: 16px;
  color: blue;
}
.module1 .tips {
  font-size: 14px;
  color: #ccccff;
}
.module2 .box {
  font-size: 18px;
  color: blue;
}
.module2 .tips {
  font-size: 16px;
  color: #6666ff;
}

可以看出,最后将所有的模块编译成了一个css文件,有了模块后我们就可以真正的按照css的结构去组织文件,而不用去担心拆分的文件过多产生的请求次数过多导致的加载性能问题。有时我们可能担心性能的问题,将整个项目的样式全部写到一个文件里,这样不利于维护。最好的方式是拆分成模块化单独写样式,然后通过impot相互引用,最后统一编译到一个文件里

注意: 预处理器中的变量是可以跨文件使用的,只要正确通过import引入

sass

除了变量符号同less不一样其他的基本一样

编译前

@import "./1-import-variable.scss";  
@import "./1-import-module1.scss";
@import "./1-import-module2.scss";

编译后

.module1 .box {
  font-size: 16px;
  color: blue;
}
.module1 .tips {
  font-size: 14px;
  color: #ccccff;
}

.module2 .box {
  font-size: 18px;
  color: blue;
}
.module2 .tips {
  font-size: 16px;
  color: #6666ff;
}

CSS预处理器框架

1.SASS - Compass

2.Less - Lesshat / EST

3.提供现成的mixin

4.类似JS类库 封装常用功能

(tips:当然自己写一些常用的mixin在项目中引用也是非常好的)

问题:分模块引入和引入所有模块有什么区别吗?

类似est这种mixin是按需调用的