预编译语言

Misaka10032CSS扩展预编译语言lesssassstylus大约 7 分钟

CSS 作为一门标记性语言,语法相对简单,对使用者的要求较低,但同时也带来一些问题

需要书写大量看似没有逻辑的代码,不方便维护及扩展,不利于复用,尤其对于非前端开发工程师来讲,往往会因为缺少 CSS 编写经验而很难写出组织良好且易于维护的 CSS 代码

CSS 预处理器便是针对上述问题的解决方案

预编译语言

扩充了 CSS 语言,增加了诸如变量、混合(mixin)、函数等功能,让 CSS 更易维护、方便

本质上,预编译语言是 CSS 的超集

包含一套自定义的语法及一个解析器,根据这些语法定义自己的样式规则,这些规则最终会通过解析器,编译生成对应的 CSS 文件

CSS 预编译语言在前端里面有三大优秀的预编处理器,分别是:

  • sass
  • less
  • stylus

sass

2007 年诞生,最早也是最成熟的 CSS 预处理器,拥有 Ruby 社区的支持和 Compass 这一最强大的 CSS 框架,目前受 LESS 影响,已经进化到了全面兼容 CSS 的 Scss

文件后缀名为.sass 与 scss,可以严格按照 sass 的缩进方式省去大括号和分号

less

2009 年出现,受 SASS 的影响较大,但又使用 CSS 的语法,让大部分开发者和设计师更容易上手,在 Ruby 社区之外支持者远超过 SASS

其缺点是比起 SASS 来,可编程功能不够,不过优点是简单和兼容 CSS,反过来也影响了 SASS 演变到了 Scss 的时代

stylus

Stylus 是一个 CSS 的预处理框架,2010 年产生,来自 Node.js 社区,主要用来给 Node 项目进行 CCSSss 预处理支持

所以 Stylus 是一种新型语言,可以创建健壮的、动态的、富有表现力的 CSS。比较年轻,其本质上做的事情与 SASS/LESS 等类似。

特性

由于各语言的功能函数各不相同,此处不作详细记录。

基本使用

  • less 和 scss
.box {
  display: block;
}
  • sass
.box  display: block
  • stylus
.box  display: block

嵌套

三者的嵌套语法都是一致的,甚至连引用父级选择器的标记 & 也相同

区别只是 Sass 和 Stylus 可以用没有大括号的方式书写

.a {
  &.b {
    color: red;
  }
}

变量

变量无疑为 CSS 增加了一种有效的复用方式,减少了原来在 CSS 中无法避免的重复「硬编码」

less 声明的变量必须以@开头,后面紧跟变量名和变量值,而且变量名和变量值需要使用冒号:分隔开

@red: #c00;
strong {
  color: @red;
}

sass 声明的变量跟 less 十分的相似,只是变量名前面使用@开头

$red: #c00;
strong {
  color: $red;
}

stylus 声明的变量没有任何的限定,可以使用$开头,结尾的分号;可有可无,但变量与变量值之间需要使用=

在 stylus 中我们不建议使用@符号开头声明变量

red = #c00
strong  color: red

作用域

CSS 预编译器把变量赋予作用域,也就是存在生命周期。就像 js 一样,它会先从局部作用域查找变量,依次向上级作用域查找

  • sass 中不存在全局变量

编译前:

$color: black;
.scoped {
  $bg: blue;
  $color: white;
  color: $color;
  background-color: $bg;
}
.unscoped {
  color: $color;
}

编译后:

.scoped {
  color: white; /*是白色*/
  background-color: blue;
}
.unscoped {
  color: white; /*白色(无全局变量概念)*/
}

所以,在 sass 中最好不要定义相同的变量名

  • less 与 stylus

less 与 stylus 的作用域跟 javascript 十分的相似,首先会查找局部定义的变量,如果没有找到,会像冒泡一样,一级一级往下查找,直到根为止

编译前:

@color: black;
.scoped {
  @bg: blue;
  @color: white;
  color: @color;
  background-color: @bg;
}
.unscoped {
  color: @color;
}

编译后:

.scoped {
  color: white; /*白色(调用了局部变量)*/
  background-color: blue;
}
.unscoped {
  color: black; /*黑色(调用了全局变量)*/
}

混入

混入(mixin)应该说是预处理器最精髓的功能之一了,简单点来说,Mixins 可以将一部分样式抽出,作为单独定义的模块,被很多选择器重复使用

可以在 Mixins 中定义变量或者默认参数

  • less

在 less 中,混合的用法是指将定义好的 ClassA 中引入另一个已经定义的 Class,也能够传递参数,参数变量为@声明

编译前:

.alert {
  font-weight: 700;
}
.highlight(@color: red) {
  font-size: 1.2em;
  color: @color;
}
.heads-up {
  .alert;
  .highlight(red);
}

编译后:

.alert {
  font-weight: 700;
}
.heads-up {
  font-weight: 700;
  font-size: 1.2em;
  color: red;
}
  • sass

sass 声明 mixins 时需要使用 @mixin,后面紧跟 mixin 的名,也可以设置参数,参数名为变量$声明的形式,调用的时候使用 @include + mixin 名称

@mixin large-text {
  font: {
    family: Arial;
    size: 20px;
    weight: bold;
  }
  color: #ff0000;
}
.page-title {
  @include large-text;
  padding: 4px;
  margin-top: 10px;
}
  • stylus

stylus 中的混合和前两款 CSS 预处理器语言的混合略有不同,他可以不使用任何符号,就是直接声明 Mixins 名,然后在定义参数和默认值之间用等号(=)来连接

error(borderWidth = 2px) {
  border: borderWidth solid #F00;
  color: #F00;
}
.generic-error {
  padding: 20px;
  margin: 4px;
  error(); /* 调用error mixins */
}
.login-error {
  left: 12px;
  position: absolute;
  top: 20px;
  error(5px); /* 调用error mixins,并将参数 borderWidth 的值指定为5px */
}

计算

预编译期允许对数字,颜色,变量的操作,支持加、减、乘、除或更复杂的综合运算

less、sass、stylus 的计算语法相同。当在属性值中使用/时,必须用圆括号括起来,为了避免某些属性对 / 运算符的误判

@file: 5%;
@base-color: lightblue;
.cor {
  width: @file * 10; //50%
  background-color: @base-color + #111;
}

@color-black: #000000;

.content {
  h3 {
    color: @color-black; // #000000
  }
  p {
    color: @color-black + #888888; // #888888
  }
}

条件判断

  • less

less 没有我们平常使用的 if,else 条件判断,而是用 when 来实现这种用法

  1. (),()相当于 JS 中的||
/* 当下边 div 中 .size 传入的第一个参数是100px或者第二个参数是100px才会执行*/
.size(@width,@height) when (@width = 100px),(@height = 100px) {
  width: @width;
  height: @height;
}

div {
  .size(100px,100px);
  background: red;
}
  1. ()and()相当于 JS 中的&&
/* 当下边 div 中 .size 传入的第一个参数是100px并且第二个参数是100px才会执行*/
.size(@width,@height) when (@width = 100px) and (@height = 100px) {
  width: @width;
  height: @height;
}

div {
  .size(100px,100px);
  background: red;
}
  • sass

sass 中的条件判断和 less 一样 sass 中也支持条件判断,只不过 sass 中的条件判断支持得更为彻底

sass 中支持的条件判断如下:

@if(条件语句){}

@else if(条件语句){}

... ...

@else(条件语句){}

sass 中当条件不为 false 或者 null 时就会执行 {} 中的代码,和 less 一样 sass 中的条件语句支持通过 >、>=、<、<=、== 进行判断

@mixin triangle($dir, $width, $color) {
  width: 0;
  height: 0;
  border-width: $width;
  border-style: solid solid solid solid;
  @if ($dir == Up) {
    border-color: transparent transparent $color transparent;
  } @else if ($dir == Down) {
    border-color: $color transparent transparent transparent;
  } @else if ($dir == Left) {
    border-color: transparent $color transparent transparent;
  } @else if ($dir == Right) {
    border-color: transparent transparent transparent $color;
  }
}

div {
  @include triangle(Left, 50px, blue);
}
  • stylus

stylus 的条件判断写法较 sass 而言更加简洁

box(x, y, margin-only = false)
  if margin-only
    margin y x
  else
    padding y x

body
  box(5px, 10px, true)

循环

  • less

less 的循环本质上是自递归调用

移动端适配方案 - less 写法

@defaultRemValue: 32px;

@defaultWidth: 750px;

.LoopScreenArray(@n, @i: 1, @argu) when (@i <= @n) {
  @value: extract(@argu, @i);

  @media only screen and (min-width: unit(@value, px)) {
    html,
    body {
      font-size: unit(
        @value / @defaultWidth * @defaultRemValue,
        px
      ) !important; /* no */
    }
  }

  .LoopScreenArray(@n, @i+1, @argu); // less的循环本质上是自递归调用
}

// 屏幕适配
.LoopScreen(@a, @b, @c, @d, @e, @f, @g, @h, @i, @j, @k, @l, @m, @n, @o, @p, @q, @r) {
  .LoopScreenArray(length(@arguments), 1, @arguments);
}

.LoopScreen(240px, 320px, 360px, 375px, 414px, 480px, 540px, 600px, 640px, 667px, 720px, 750px, 768px, 800px, 834px, 1024px, 1080px, 1440px);
  • sass

sass 中直接支持循环语句,分别是 for 循环和 while 循环

ul {
  li {
    width: 100%;
    height: 50px;
    border: 1px solid #000;
    font-size: 20px;
    color: #fff;
    background: red;
    // 5、6、7、8
    @for $i from 5 through 8 {
      &:nth-child(#{$i}) {
        background: deepskyblue;
      }
    }
  }
}
ul {
  li {
    width: 100%;
    height: 50px;
    border: 1px solid #000;
    font-size: 20px;
    color: #fff;
    background: red;
    // 5、6、7
    @for $i from 5 to 8 {
      &:nth-child(#{$i}) {
        background: deepskyblue;
      }
    }
  }
}

两者的区别 through 包头包尾,to 包头不包尾

ul {
  li {
    width: 100%;
    height: 50px;
    border: 1px solid #000;
    font-size: 20px;
    color: #fff;
    background: red;
    $i: 5;
    @while ($i <= 8) {
      &:nth-child(#{$i}) {
        background: deepskyblue;
      }
      $i: $i + 1;
    }
  }
}
  • stylus

stylus 允许通过 for/in 对表达式进行迭代形式如下

for <val-name> [, <key-name>] in <expression>

for num in (1..10)
  .box{num}
    animation: box + num 5s infinite

@keframes box{num}
  0%   { left: 0px }
  100% { left: (num * 30px) }

模块化 import

模块化就是将 Css 代码分成一个个模块

sass、less、stylus 三者的使用方法都如下所示

@import "./common";
@import "./github-markdown";
@import "./mixin";
@import "./variables";