预编译:less,sass,stylus

预处理语言的诞生

其中 就我所知的有三门语言:Sass、Less 、Stylus 。

  1. Sass 诞生于 2007 年,Ruby 编写,其语法功能都十分全面,可以说 它完全把 CSS 变成了一门编程语言。另外 在国内外都很受欢迎,并且它的项目团队很是强大 ,是一款十分优秀的预处理语言。
  2. Stylus 诞生于 2010 年,来自 Node.js 社区,语法功能也和 Sass 不相伯仲,是一门十分独特的创新型语言。
  3. Less 诞生于 2009 年,受 Sass 的影响创建的一个开源项目。 它扩充了 CSS 语言,增加了诸如变量、混合(mixin)、函数等功能,让 CSS 更易维护、方便制作主题、扩充(引用于官网)。

选择预处理语言

这是一个十分纠结的问题。

  1. 在网上讨论看来,Sass 与 Stylus 相比于 Less 功能更为丰富,但对于学习成本以及适应时间 ,Less 稍胜一筹,这也是我选择 Less 的原因。
  2. Less 没有去掉任何 CSS 的功能,而是在现有的语法上,增添了许多额外的功能特性,所以学习 Less 是一件非常舒服的事情。

sass 用法

1.基本用法

1 >变量

SASS 允许使用变量,所有变量以$开头。

 $blue : #1875e7; 

  div {
   color : $blue;
  }

如果变量需要镶嵌在字符串之中,就必须需要写在#{}之中。

 $side : left;

  .rounded {
    border-#{$side}-radius: 5px;
  }

2> 计算功能

SASS 允许在代码中使用算式:

body {
    margin: (14px/2);
    top: 50px + 100px;
    right: $var * 10%;
  }

3> 嵌套

SASS 允许选择器嵌套。比如,下面的 CSS 代码:

 div h1 {
    color : red;
  }

可以写成:

 div {
    hi {
      color:red;
    }
  }

属性也可以嵌套,比如 border-color 属性,可以写成:

 p {
    border: {
      color: red;
    }
  }

注意,border 后面必须加上冒号。
在嵌套的代码块内,可以使用&引用父元素。比如 a:hover 伪类,可以写成:

a {
    &:hover { color: #ffb3ff; }
  }

4>注释

SASS 共有两种注释风格。
标准的 CSS 注释 /_ comment _/ ,会保留到编译后的文件。
单行注释 // comment,只保留在 SASS 源文件中,编译后被省略。
在/*后面加一个感叹号,表示这是"重要注释"。即使是压缩模式编译,也会保留这行注释,通常可以用于声明版权信息。

	 /*!
    重要注释!
  */

2.代码的重用

1> 继承

SASS 允许一个选择器,继承另一个选择器。比如,现有 class1:

.class1 {
    border: 1px solid #ddd;
  }

class2 要继承 class1,就要使用@extend 命令:

.class2 {
    @extend .class1;
    font-size:120%;
 }

2> Mixin

Mixin 有点像 C 语言的宏(macro),是可以重用的代码块。
使用@mixin 命令,定义一个代码块。

@mixin left {
    float: left;
    margin-left: 10px;
  }

使用@include 命令,调用这个 mixin。

div {
    @include left;
  }

mixin 的强大之处,在于可以指定参数和缺省值。

@mixin left($value: 10px) {
    float: left;
    margin-right: $value;
  }

使用的时候,根据需要加入参数:

div {
    @include left(20px);
  }

下面是一个 mixin 的实例,用来生成浏览器前缀。

@mixin rounded($vert, $horz, $radius: 10px) {
    border-#{$vert}-#{$horz}-radius: $radius;
    -moz-border-radius-#{$vert}#{$horz}: $radius;
    -webkit-border-#{$vert}-#{$horz}-radius: $radius;
  }

使用的时候,可以像下面这样调用:

#navbar li { @include rounded(top, left); }
#footer { @include rounded(top, left, 5px); }

3> 颜色函数

SASS 提供了一些内置的颜色函数,以便生成系列颜色。

	 lighten(#cc3, 10%) // #d6d65c
  darken(#cc3, 10%) // #a3a329
  grayscale(#cc3) // #808080
  complement(#cc3) // #33c

4> 插入文件

@import 命令,用来插入外部文件。

@import "path/filename.scss";

如果插入的是.css 文件,则等同于 css 的 import 命令。

@import "foo.css";

3.高级用法

1> 条件语句

@if 可以用来判断:

p {
    @if 1 + 1 == 2 { border: 1px solid; }
    @if 5 < 3 { border: 2px dotted; }
  }

配套的还有@else 命令:

@if lightness($color) > 30% {
    background-color: #000;
  } @else {
    background-color: #fff;
  }

2> 循环语句

SASS 支持 for 循环:

@for $i from 1 to 10 {
    .border-#{$i} {
      border: #{$i}px solid blue;
    }
  }

也支持 while 循环:

$i: 6;
  @while $i > 0 {
    .item-#{$i} { width: 2em * $i; }
    $i: $i - 2;
  }

each 命令,作用与 for 类似:

@each $member in a, b, c, d {
    .#{$member} {
      background-image: url("/image/#{$member}.jpg");
    }
  }

3> 自定义函数

SASS 允许用户编写自己的函数。

@function double($n) {
    @return $n * 2;
  }
  #sidebar {
    width: double(5px);
  }

关于 sass 的常见使用总结:

1>变量   

//定义变量
$blue : #1875e7;
$side : left;

//常规使用
div {   
  		color : $blue;
 		}

//字符串中使用 #{}
.rounded {
  border-#{$side}-radius: 5px;
}

2>继承

使用@extend 来实现一个类对另一个类的继承

.class1 {
    border: 1px solid #ddd;
 }

//class2 继承 class1,使用 @extend
.class2 {
    @extend .class1;
    font-size:120%;
 }

3>mixin

使用@mixin 定义一个代码块,使用@include 命令,调用这个 mixin

//常规使用
@mixin left{
	  float: left;
   margin-left: 10px;
}

div {
  @include left;
}

//用来生成浏览器的前缀,可设置变量和变量默认值
@mixin rounded($vert, $horz, $radius: 10px) {
    border-#{$vert}-#{$horz}-radius: $radius;
    -moz-border-radius-#{$vert}#{$horz}: $radius;
    -webkit-border-#{$vert}-#{$horz}-radius: $radius;
}

#navbar li {
	@include rounded(top, left);
}

#footer {
	@include rounded(top, left, 5px);
}

** 4>自定义函数**

@function double($n) {
 @return $n * 2;
}
#sidebar {
  width: double(5px);
}

less 用法:

变量

1>值变量

@color: #999;
#wrap {
  color: @color;
}

2>选择器变量(变量名 必须使用大括号包裹 @{变量名})

@Wrap: wrap;
@mySelector: #wrap;

@{mySelector}{
  color: #999;
  width: 50%;
}

.@{Wrap}{
  color:#ccc;
}
#@{Wrap}{
  color:#666;
}


3>属性变量(变量名 必须使用大括号包裹 @{变量名})

@borderStyle: border-style;
@Soild: solid;

#wrap {
  @{borderStyle}: @Soild; //变量名 必须使用大括号包裹
}

4> url 变量

@images: "../img"; //需要加引号
body {
  background: url("@{images}/dog.png"); //变量名 必须使用大括号包裹
}

5>声明变量(引用一段代码)
有点类似于 下面的 混合方法

  • 结构: @name: { 属性: 值 ;};
  • 使用:@name();
@background: {
  background: red;
};

#main {
  @background();
}

6>变量运算

  • 加减法时 以第一个数据的单位为基准
  • 乘除法时 注意单位一定要统一
@width: 300px;
@color: #222;

#wrap {
  width: @width-20;
  height: @width-20*5;
  margin: (@width-20) * 5;
  color: @color*2;
  background-color: @color + #111;
}

/* 生成的 CSS */
#wrap {
  width: 280px;
  height: 200px;
  margin: 1400px;
  color: #444;
  background-color: #333;
}

7>变量作用域
一句话理解就是:就近原则,不要跟我提闭包。

@var: @a;
@a: 100%;
#wrap {
  width: @var;
  @a: 9%;
}

/* 生成的 CSS */
#wrap {
  width: 9%;
}

8>用变量去定义变量

/* Less */
@fnord: "I am fnord.";
@var: "fnord";
#wrap::after {
  content: @@var; //将@var替换为其值 content:@fnord;
}
/* 生成的 CSS */
#wrap::after {
  content: "I am fnord.";
}

嵌套

1> & 的妙用
& :代表的上一层选择器的名字,此例便是header

/* Less */
#header {
  &:after {
    content: "Less is more!";
  }
  .title {
    font-weight: bold;
  }
  &_content {
    //理解方式:直接把 & 替换成 #header
    margin: 20px;
  }
}
/* 生成的 CSS */
#header::after {
  content: "Less is more!";
}
#header .title {
  //嵌套了
  font-weight: bold;
}
#header_content {
  //没有嵌套!
  margin: 20px;
}

2>媒体查询
在以往的工作中,我们使用 媒体查询,都要把一个元素 分开写

#wrap {
  width: 500px;
}
@media screen and (max-width: 768px) {
  #wrap {
    width: 100px;
  }
}
  • Less 提供了一个十分便捷的方式
/* Less */
#main {
  //something...
  @media screen {
    @media (max-width: 768px) {
      width: 100px;
    }
  }
  @media tv {
    width: 2000px;
  }
}
/* 生成的 CSS */
@media screen and (maxwidth: 768px) {
  #main {
    width: 100px;
  }
}
@media tv {
  #main {
    width: 2000px;
  }
}
  • 唯一的缺点就是 每一个元素都会编译出自己 @media 声明,并不会合并。

3>实战技巧
可以借助 Less 在元素中,去定义自己的私有样式。

/* Less */
#main {
  // something..
  &.show {
    display: block;
  }
}
.show {
  display: none;
}
const main = document.getElementById("main");
main.classList.add("show");
复制代码
  • 结果:
#main.show {
  display: block;
}
.show {
  display: none; //会被覆盖。
}

混合方法

  1. 无参数方法方法犹如 声明的集合,使用时 直接键入名称即可。
/* Less */
.card { // 等价于 .card()
    background: #f6f6f6;
    -webkit-box-shadow: 0 1px 2px rgba(151, 151, 151, .58);
    box-shadow: 0 1px 2px rgba(151, 151, 151, .58);
}
#wrap{
  .card;//等价于.card();
}
/* 生成的 CSS */
#wrap{
  background: #f6f6f6;
  -webkit-box-shadow: 0 1px 2px rgba(151, 151, 151, .58);
  box-shadow: 0 1px 2px rgba(151, 151, 151, .58);
}
复制代码
  1. 其中 .card.card() 是等价的。 个人建议,为了避免 代码混淆,应写成 :
.card(){
  //something...
}
#wrap{
  .card();
}
复制代码
  1. 要点:
    • .# 皆可作为 方法前缀。
    • 方法后写不写 () 看个人习惯。
  2. 默认参数方法
    • Less 可以使用默认参数,如果 没有传参数,那么将使用默认参数。
    • @arguments 犹如 JS 中的 arguments 指代的是 全部参数。
    • 传的参数中 必须带着单位。
/* Less */
.border(@a:10px,@b:50px,@c:30px,@color:#000){
    border:solid 1px @color;
    box-shadow: @arguments;//指代的是 全部参数
}

#main{
    .border(0px,5px,30px,red);//必须带着单位
}

#wrap{
    .border(0px);
}

#content{
  .border;//等价于 .border()
}

/* 生成的 CSS */
#main{
    border:solid 1px red;
    box-shadow:0px,5px,30px,red;
}
#wrap{
    border:solid 1px #000;
    box-shadow: 0px 50px 30px #000;
}
#content{
    border:solid 1px #000;
    box-shadow: 10px 50px 30px #000;
}
复制代码
  1. 方法的匹配模式与 面向对象中的多态 很相似
/* Less */
.triangle(top,@width:20px,@color:#000) {
  border-color: transparent transparent @color transparent;
}
.triangle(right,@width:20px,@color:#000) {
  border-color: transparent @color transparent transparent;
}
.triangle(bottom,@width:20px,@color:#000) {
  border-color: @color transparent transparent transparent;
}
.triangle(left,@width:20px,@color:#000) {
  border-color: transparent transparent transparent @color;
}
.triangle(@_,@width:20px,@color:#000) {
  border-style: solid;
  border-width: @width;
}

#main {
  .triangle(left, 50px, #999);
}
/* 生成的 CSS */
#main {
  border-color: transparent transparent transparent #999;
  border-style: solid;
  border-width: 50px;
}
  1. 要点
    • 第一个参数 left 要会找到方法中匹配程度最高的,如果匹配程度相同,将全部选择,并存在着样式覆盖替换。
    • 如果匹配的参数 是变量,则将会匹配,如 @_
  2. 方法的命名空间让方法更加规范
/* Less */
#card(){
    background: #723232;
    .d(@w:300px){
        width: @w;

        #a(@h:300px){
            height: @h;//可以使用上一层传进来的方法
        }
    }
}
#wrap{
    #card > .d > #a(100px); // 父元素不能加 括号
}
#main{
    #card .d();
}
#con{
    //不得单独使用命名空间的方法
    //.d() 如果前面没有引入命名空间 #card ,将会报错

    #card; // 等价于 #card();
    .d(20px); //必须先引入 #card
}

/* 生成的 CSS */
#wrap{
  height:100px;
}
#main{
  width:300px;
}
#con{
  width:20px;
}
复制代码
  1. 要点
    • 在 CSS 中> 选择器,选择的是 儿子元素,就是 必须与父元素 有直接血源的元素。
    • 在引入命令空间时,如使用 > 选择器,父元素不能加 括号。
    • 不得单独使用命名空间的方法 必须先引入命名空间,才能使用 其中方法。
    • 子方法 可以使用上一层传进来的方法
  2. 方法的条件筛选Less 没有 if else,可是它有 when
/* Less */
#card{

    // and 运算符 ,相当于 与运算 &&,必须条件全部符合才会执行
    .border(@width,@color,@style) when (@width>100px) and(@color=#999){
        border:@style @color @width;
    }
    // not 运算符,相当于 非运算 !,条件为 不符合才会执行
    .background(@color) when not (@color>=#222){
        background:@color;
    }
    // , 逗号分隔符:相当于 或运算 ||,只要有一个符合条件就会执行
    .font(@size:20px) when (@size>50px) , (@size<100px){
        font-size: @size;
    }
}
#main{
    #card>.border(200px,#999,solid);
    #card .background(#111);
    #card > .font(40px);
}
/* 生成后的 CSS */
#main{
  border:solid #999 200px;
  background:#111;
  font-size:40px;
}
复制代码
  1. 要点
    • 比较运算有: > >= = =< <。
    • = 代表的是等于
    • 除去关键字 true 以外的值都被视为 false:
  2. 数量不定的参数
    如果你希望你的方法接受数量不定的参数,你可以使用... ,犹如 ES6 的扩展运算符。
/* Less */
.boxShadow(...){
    box-shadow: @arguments;
}
.textShadow(@a,...){
    text-shadow: @arguments;
}
#main{
    .boxShadow(1px,4px,30px,red);
    .textShadow(1px,4px,30px,red);
}
/* 生成后的 CSS */
#main{
  box-shadow: 1px 4px 30px red;
  text-shadow: 1px 4px 30px red;
}
复制代码
  1. 方法使用 important!
    使用方法 非常简单,在方法名后 加上关键字即可。
/* Less */
.border{
    border: solid 1px red;
    margin: 50px;
}
#main{
    .border() !important;
}
/* 生成后的 CSS */
#main {
    border: solid 1px red !important;
    margin: 50px !important;
}
复制代码
  1. 循环方法
    Less 并没有提供 for 循环功能,但这也难不倒 聪明的程序员,使用递归去实现。 下面是官网中的一个 Demo,模拟了生成栅格系统。
/* Less */
.generate-columns(4);
.generate-columns(@n, @i: 1) when (@i =< @n) {
  .column-@{i} {
    width: (@i * 100% / @n);
  }
  .generate-columns(@n, (@i + 1));
}
/* 生成后的 CSS */
.column-1 {
  width: 25%;
}
.column-2 {
  width: 50%;
}
.column-3 {
  width: 75%;
}
.column-4 {
  width: 100%;
}
复制代码
  1. 属性拼接方法+_ 代表的是 空格;+ 代表的是 逗号。
    • 逗号
/* Less */
.boxShadow() {
    box-shadow+: inset 0 0 10px #555;
}
.main {
  .boxShadow();
  box-shadow+: 0 0 20px black;
}
/* 生成后的 CSS */
.main {
  box-shadow: inset 0 0 10px #555, 0 0 20px black;
}
复制代码
  • 空格
/* Less */
.Animation() {
  transform+_: scale(2);
}
.main {
  .Animation();
  transform+_: rotate(15deg);
}
/* 生成的 CSS */
.main {
  transform: scale(2) rotate(15deg);
}
复制代码
  1. 实战技巧
    下面是官网中的一个非常赞的 Demo
/* Less */
.average(@x, @y) {
  @average: ((@x + @y) / 2);
}
div {
  .average(16px, 50px); // 调用 方法
  padding: @average;    // 使用返回值
}
/* 生成的 CSS */
div {
  padding: 33px;
}
复制代码

可以说 Less 是一门优雅编程语言。

继承

extend 是 Less 的一个伪类。它可继承 所匹配声明中的全部样式。

  1. extend 关键字的使用
/* Less */
.animation {
  transition: all 0.3s ease-out;
  .hide {
    transform: scale(0);
  }
}
#main {
  &:extend(.animation);
}
#con {
  &:extend(.animation .hide);
}
/* 生成后的 CSS */
.animation,
#main {
  transition: all 0.3s ease-out;
}
.animation .hide,
#con {
  transform: scale(0);
}
  1. all 全局搜索替换
    使用选择器匹配到的 全部声明。
/* Less */
#main{
  width: 200px;
}
#main {
  &:after {
    content:"Less is good!";
  }
}
#wrap:extend(#main all) {}
/* 生成的 CSS */
#main,#wrap{
  width: 200px;
}
#main:after, #wrap:after {
    content: "Less is good!";
}
复制代码
  1. 减少代码的重复性
    从表面 看来,extend 与 方法 最大的差别,就是 extend 是同个选择器共用同一个声明,而 方法 是使用自己的声明,这无疑 增加了代码的重复性。
    方法示例 与上面的 extend 进行对比:
/* Less */
.Method{
  width: 200px;
  &:after {
      content:"Less is good!";
  }
}
#main{
  .Method;
}
#wrap{
  .Method;
}
/* 生成的 CSS */
#main{
  width: 200px;
  &:after{
    content:"Less is good!";
  }
}
#wrap{
  width: 200px;
  &:after{
    content:"Less is good!";
  }
}
复制代码
  1. 要点翻译官网
    • 选择器和扩展之间 是允许有空格的:pre:hover :extend(div pre).
    • 可以有多个扩展: pre:hover:extend(div pre):extend(.bucket tr) - 注意这与 pre:hover:extend(div pre, .bucket tr)一样。
    • 这是不可以的,扩展必须在最后 : pre:hover:extend(div pre).nth-child(odd)。
    • 如果一个规则集包含多个选择器,所有选择器都可以使用 extend 关键字。
  2. 导入
    1. 导入 less 文件 可省略后缀
import "main";
//等价于
import "main.less";
复制代码
  1. @import 的位置可随意放置
#main{
  font-size:15px;
}
@import "style";
复制代码
  1. reference
    Less 中 最强大的特性 使用 引入的 Less 文件,但不会 编译它。
/* Less */
@import (reference) "bootstrap.less";
#wrap:extend(.navbar all) {
}
  1. 翻译官网:

    使用@import (reference)导入外部文件,但不会添加 把导入的文件 编译到最终输出中,只引用。

  2. once

    @import 语句的默认行为。这表明相同的文件只会被导入一次,而随后的导入文件的重复代码都不会解析。

@import (once) "foo.less";
@import (once) "foo.less"; // this statement will be ignored
复制代码
  1. multiple

    使用@import (multiple)允许导入多个同名文件。

/* Less */
// file: foo.less
.a {
  color: green;
}
// file: main.less
@import (multiple) "foo.less";
@import (multiple) "foo.less";
/* 生成后的 CSS */
.a {
  color: green;
}
.a {
  color: green;
}

函数

  1. 判断类型
    • isnumber

      判断给定的值 是否 是一个数字。

isnumber(#ff0);     // false
isnumber(blue);     // false
isnumber("string"); // false
isnumber(1234);     // true
isnumber(56px);     // true
isnumber(7.8%);     // true
isnumber(keyword);  // false
isnumber(url(...)); // false
复制代码
  • iscolor

    判断给定的值 是否 是一个颜色。

  • isurl

    判断给定的值 是否 是一个 url 。

  1. 颜色操作

    • saturate

      增加一定数值的颜色饱和度。

    • lighten

      增加一定数值的颜色亮度。

    • darken

      降低一定数值的颜色亮度。

    • fade

      给颜色设定一定数值的透明度。

    • mix

      根据比例混合两种颜色。

  2. 数学函数

    • ceil

      向上取整。

    • floor

      向下取整。

    • percentage

      将浮点数转换为百分比字符串。

    • round

      四舍五入。

    • sqrt

      计算一个数的平方根。

    • abs

      计算数字的绝对值,原样保持单位。

    • pow

      计算一个数的乘方。

由于 文章 篇幅有限,所以 只能介绍一些 使用效率高的函数。
如果你想了解更多,可以去官网的函数链接

其他

  1. 注释
    • /* */ CSS 原生注释,会被编译在 CSS 文件中。
    • /   / Less 提供的一种注释,不会被编译在 CSS 文件中。
  2. 避免编译
/* Less */
#main{
  width:~'calc(300px-30px)';
}
/* 生成后的 CSS */
#main{
  width:calc(300px-30px);
}
复制代码
  1. 结构: ~' 值 '
  2. 变量拼串
    在平时工作中,这种需求 太常见了。 在下面例子中, 实现了不同的 transtion-delay、animation、@keyframes
.judge(@i) when(@i=1){
  @size:15px;
}
.judge(@i) when(@i>1){
  @size:16px;
}
.loopAnimation(@i) when (@i<16) {

  .circle:nth-child(@{i}){
      .judeg(@i);
      border-radius:@size @size 0 0;
      animation: ~"circle-@{i}" @duration infinite @ease;
      transition-delay:~"@{i}ms";
  }
  @keyframes ~"circle-@{i}" {
      // do something...
  }
  .loopAnimation(@i + 1);
}
复制代码
  1. 结构: ~"字符@{变量}字符";
  2. 使用 JS
    因为 Less 是由 JS 编写,所以 Less 有一得天独厚的特性:代码中使用 Javascript 。
/* Less */
@content:` "aaa".toUpperCase()`;
#randomColor {
  @randomColor: ~"rgb(`Math.round(Math.random() * 256)`,`Math.round(Math.random() * 256)`,`Math.round(Math.random() * 256)`)";
}
#wrap {
  width: ~"`Math.round(Math.random() * 100)`px";
  &:after {
    content: @content;
  }
  height: ~"`window.innerHeight`px";
  alert: ~"`alert(1)`";
  #randomColor();
  background-color: @randomColor;
}
/* 生成后的 CSS */
// 弹出 1
#wrap {
  width: 随机值(0~100)px;
  height: 743px; //由电脑而异
  background: 随机颜色;
}
#wrap::after {
  content: "AAA";
}

文章引用:

https://www.ruanyifeng.com/blog/2012/06/sass.html
https://juejin.im/post/6844903520441729037

Q.E.D.