Less, 更加高效好用的动态CSS(i)

为什么会有less的出现

如果要我说什么语言是写起来最烦人的,我恐怕会毫无疑问的说——肯定是CSS。原因恐怕是每个写过CSS的人很能理解的。简单的几十行代码还好,如果要是几百行上千行的CSS代码,HTML中一个div套着另一个div估计能套个十层,那种CSS代码真的非常的难以阅读了,除非是真的是隔几个样式协议注释这种的,要不然看起来是真的费劲。一千行的HTML代码看起来就是非常的清晰,因为他有结构,而CSS代码没有任何的结构可言,选择器一个套着一个,看起来非常的赘余,我们是否可以想写HTML那样写CSS呢?这是我刚开始写CSS的时候就在思考的问题。

Less

Less是一门动态的CSS样式语言。正如他的名字那样,less的出现就是为了使我们写更加少的CSS代码,不仅如此,少的CSS代码还可以发挥更强大的作用。其实我们写的也不是原生的CSS的语法,而是使用less的语法,less的编译器会将我们的less语法编译成为CSS的语法,也就是说浏览器最终渲染的还是CSS,less只是一个中间产物罢了,不过这种中间产物可是一个开发利器。下面就从less的功能开始说起。

如何使用less

使用less有如下的三种方式。

  • 第一种是通过js在运行时渲染less文件。

    <link rel="stylesheet/less" type="text/css" href="styles.less" />
    <script src="//cdnjs.cloudflare.com/ajax/libs/less.js/3.10.0-beta/less.min.js" ></script>

    上面使用cdn的方式引用了less.min.js,同样的我们也可以通过下载这个文件到本地的方式进行引用。

    需要注意的是上面的linkrel中的值是stylesheet/less,不是普通的stylesheet,为什么要如此呢?因为less.min.js需要找到这个属性的标签中的内容进行解析然后渲染到页面中。既然是使用js进行渲染的,那么肯定就是在运行的时候做的事情,这就极大的降低的效率,这种方式不是很推荐。不过如果就是写那么一点点less代码这种方式也是可以的。

  • 第二种方式在Node.js的环境中使用less

    先通过npm install -g less的命令安装less(前提是你的电脑中需要安装Nodejs而且你还是需要将其加入到环境变量中。然后通过lessc style.less style.css就可以将style.less这样的一个less文件编译成为style.css的css文件进行使用了。

  • 第三种方式和第二种方式有点儿相似。

    不过这里我们不需要安装Nodejs的环境,我们只需要下载一个less的桌面的实时编译软件。(如我现在使用的koala软件就具有这样的功能。)我们将CSS文件夹放入到该软件中,软件就会自动的讲我们写好的less文件编译成为同名的css文件。此时我们引用的是那个css文件,而不是less文件。同样的,在页面中,我们也不需要引入那个什么less.min.js文件对我们的less代码进行渲染了。因为我们给浏览器本身就是CSS文件。

    这种方式也是现在我在使用的。不过说到底还是第二种方式最好吧。

结构化的嵌套

正如我上面说到的问题,CSS代码没有结构,如果我们可以将CSS样式中进行嵌套那就好了。

比如说如下的CSS代码。

div.box {
  background-color: red;
  width: 200px;
  height: 200px;
}

div.box .minbox {
  background-color: aqua;
  width: 100px;
  height: 100px;
}

div.box p {
  font-size: 20px;
  color: blue;
}

div.box:hover {
  background-color: skyblue;
}

现在的CSS代码还非常的少所以我们很容易的看清楚代码的结构,不过如果我们可以这样写的话,结构表现的可能就会更美好了。

div.box{
    background-color: red;
    width: 200px;
    height: 200px;

    .minbox{
        background-color: aqua;
        width: 100px;
        height: 100px;
    }

    p{
        font-size: 20px;
        color: blue;
    }

    &:hover{
        background-color: skyblue;
    }
}

CSS的结构看的是一目了然,而且我们还可以通过像HTML那样代码的折叠观察我们真正需要注意的那部分CSS代码。可惜的是原生的CSS语法并不能满足我们这样的要求。不过试问,那个小大哥小大姐不想要这样子写代码呢?

less就提供了这样的写法。可以说认识了less之后,你就再也不想写原生的CSS代码呢,那简直就是一种酷刑。

上面的结构化嵌套的CSS代码其实就是使用less的语法写出来的。很容易理解和学习,不过如果仔细地观察的话,我们会看到一个非常默认的东西,那就是&。因为写在div中的选择器选择的都是他的后代,不过我们是要div:hover这样伪选择器的需要的,如果我们就是直接写一个:hover就是相当于div :hover,这个是给div的所有的子类加上了一个hover,这样做肯定是不可以的。此时我们就需要使用&

&表示的是当前选择器的父级,这样&:hover就就是代表div:hover,此时就是给div加上了hover。

CSS中非常常见的一个类clearfix使用less语法可以写成下面的这样。

.clearfix{
    display: block;
    zoom: 1; // 兼容旧版的ie

    &:before, &:after{ // 解决高度塌陷和子元素外边距传递的问题
        content: "";
        display: table;
        clear: both;
        font-size: 0;
        height: 0;
        visibility: hidden;
    }

}

通过less的编译之后生成了如下的代码。

.clearfix {
  display: block;
  zoom: 1;
}

.clearfix:before,
.clearfix:after {
  content: "";
  display: table;
  clear: both;
  font-size: 0;
  height: 0;
  visibility: hidden;
}

更多关于&

div.link {
  & + & {
    color: red;
  }

  & & {
    color: green;
    &666{
        color: red;
    }
  }

  && {
    color: blue;
    &777{
        color: #00FFFF;
    }
  }

  &, &ish {
    color: cyan;
  }
  & > span {
      &:hover {
          color: white;
      }
  }
}

观察上面的选择器,有的都用了不止一个的&,如果使用多个&会发生什么呢?那就要看编译结果了。

div.link + div.link {
  color: red;
}
div.link div.link {
  color: green;
}
div.link div.link666 {
  color: red;
}
div.linkdiv.link {
  color: blue;
}
div.linkdiv.link777 {
  color: #00FFFF;
}
div.link,
div.linkish {
  color: cyan;
}
div.link > span:hover {
  color: white;
}

很显然所有的&都被直接替换成为了他们父级的选择器,如div.link

需要的是替换的不只是父级的哪一个选择器,而是所有的选择器。

.grand {
  .parent {
    & > & {
      color: red;
    }

    & & {
      color: green;
    }

    && {
      color: blue;
    }

    &, &ish {
      color: cyan;
    }
  }
}

被编译成了

.grand .parent > .grand .parent {
  color: red;
}
.grand .parent .grand .parent {
  color: green;
}
.grand .parent.grand .parent {
  color: blue;
}
.grand .parent,
.grand .parentish {
  color: cyan;
}

注释

在原生CSS中只有一种注释,那就是多行注释/* */,不过在less中我们也可以使用大多数语言使用的单行注释//了,这算是对我们这些喜欢使用//作为注释的一个体验上面简单的一个优化。

不过这还不是单行多行这么简单的一个区别。如果我们使用的是多行注释的话,使用less编译默认是将我们的注释编译到css文件中的,而单行注释是不会别编译到css文件中的。(所以什么见不得人的东西要写在单行注释中哦)。不过我们也可以修改less的编译选项,使其也不编译多行注释,甚至我们可以让他编译出来的CSS文件是一个compressed(压缩)过的版本。

变量

变量的基本使用

原生CSS中还有一个非常令人头疼的事情,就是没有变量。比如说如果我们在网页中经常要使用到一种颜色#ff6d3f,我们就得把这个颜色背下来,然后在需要的地方写下color: #ff6d3f或者backgroud: #ff6d3f。有一天突然又感觉这个颜色不是很好,于是又要换成#ee6dfe。就算是使用ctrl+h进行替换也是一件非容令人头疼的事情。这个时候有一个变量的好处就体现出来了。

@width: 10px;
@height: @width + 10;
@color: red;

#header {
    width: @width;
    height: @height;
    background-color: @color;
}

这样的less代码会被编译成为如下的CSS代码。

#header {
  width: 10px;
  height: 20px;
  background-color: #ff0000;
}

通过@xxx: xxx;这种方式我们就可以定义一个变量了。不过我们是否可以使用一个变量作为一个属性的名字呢?或者是说作为选择器的名字呢?

尝试下面的这种写法。

#header {
    width: @width;
    height: @height;
    background-color: @color;
    @m: 100px;

    @d {
        background-color: @color;
    }
}

想象是非常的美好的,但是现实往往是非常的骨感。观察一下编译的结果。

#header {
  width: 10px;
  height: 20px;
  background-color: #ff0000;
}
@d {
  background-color: #ff0000;
}

@m这个属性发现都没有了,而@d这个选择器好像也要闹独立了,而且名字竟然还是@d,你说气人不气人。

经过查阅资料我们才知道,如果要使用选择器或者是属性名字的变量,在使用的时候需要加上大括号,比如上面的代码的正确的写法应该是这样子的。

#header {
    width: @width;
    height: @height;
    background-color: @color;
    @{m}: 100px;

    @{d} {
        background-color: @color;
    }
}

此时的编译结果如下。

#header {
  width: 10px;
  height: 20px;
  background-color: #ff0000;
  margin: 100px;
}
#header div {
  background-color: #ff0000;
}

这就是我们想要的结果。不过你可能带有疑问的是,为啥我们要将属性或者是选择器作为变量来使用呢?现在讨论这个问题有点儿太早了,等之后我们看bootstrap源码的时候就会知道这个东西有什么用了,这可是高端操作。

不仅仅是选择器和属性名,变量还有下面的这些用法。

// 作为URL的一部分
@img: "../img/myimg";

div {
    background: url("@{img}/hello.png")
}

// 作为import的一部分
// Variables
@themes: "../../src/themes";

// Usage
@import "@{themes}/tidal-wave.less";

// 我们可以通过变量引用另一个变量,不过这种写法确实是不常见
@primary:  green;
@secondary: blue;

.section {
  @color: primary;

  .element {
    color: @@color;
    // @color 是primary @@color也就是@@color->@primary->green
  }
}

变量的懒运算

首先我们来一段JavaScript的代码。下面的代码是否会报错呢?如果不报错又会输出什么东西呢?

var a = b
var b = 3
console.log('a = ', a)
// 输出还是报错呢?输出什么呢?

估计只要是稍微有一点JavaScript的人都知道上面的代码是不会报错的,而是输出undefined。JavaScript运行的时候首先来处理变量的声明,被声明的变量还没有被定义,值为undefined。也就说上面的代码相当于这样子的。

var a = undefined
var b = undefined
a = b
b = 3
console.log('a = ', a)

为嘛要说到JavaScript中的这个玩意呢?那是因为less中的所谓的变量也有这么一手,不过两者是不怎么一样的。

.lazy-eval {
  width: @var;
  @a: 9%;
}

@var: @a;
@a: 100%;

上面的less变量,我们也是在变量定义之前先来使用他的,这并没有报错,编译结果如下。

.lazy-eval {
  width: 9%;
}

没有任何的问题,那是因为less会讲所有的变量声明的语句放在前面去执行,类似的@import语句也会放在最前面去执行,和JavaScript不同的是,没有所谓的undefined了。各种不同的变量可以和谐的相处。不过如果是相同的变量在同一个作用域中声明了两次的话,后面的还是会覆盖前面的声明的。如

.header {
  --color: white;
  color: var(--color);  // the color is black
  --color: black;
}

变量的作用域

上面说到了在同一个作用域中声明同一个变量,那么不在同一个作用域中的同名是不是也会覆盖呢?多种其他语言的编程经验告诉我们这是不可能会进行覆盖的。比如上面的列中的两个@a的声明,不过这个还不够典型。看下面的这个例子。

@var: 0;
.class {
  @var: 1;
  .brass {
    @var: 2;
    three: @var;
    @var: 3;
  }
  one: @var;
}

编译的结果如下。

.class {
  one: 1;
}
.class .brass {
  three: 3;
}

和我们之前的猜想是一致的,大括号中就是一个作用域。内部的作用域和外部的作用域的同名的变量是相互不影响,不会覆盖的,但是内部如果不是同名的变量的话,还是可以使用外部的变量的。

很多情况下这种写法也可以节省很多的代码量。

我们不仅仅可以在less中使用变量,可以看到的是,我们甚至还可以对这些变量进行加减乘除的运算。如上面的@height: @width + 10;就使用加法的运算。不过这个运算似乎是有点儿奇怪,因为看起来是10px + 10,一个是有单位的量,而另一个是没有单位的量,这样真的可以进行运算吗?答案是可以的,只要一个有单位就可以了哦。比如10 * 20px的结果是200px。只需要一个有单位。

避免编译

对于上面的那个less中表达式的计算,现在又出现了一个问题,就是有些东西我不想less帮我进行编译,我想要css来计算他。比如说使用@width: calc(100% + 10px);

我们本来的目的想要想要其比父元素的大小还要大10px,经过less的编译之后变成了width: calc(110%);这个不是搞事情的嘛,这个地方less你就不要好心给我们办坏事了,这里不需要你的编译。这个时候我们就可以使用less避免编译的语法。

~'不需要less编译的内容'

上面的代码就可以写成@width: ~'calc(100% + 10px)';编译的结果为width: calc(100% + 10px);

不过此时又出现了一个问题。那就是@height: @width + 10这行代码会报错,因为less没有给你进行编译,less也不知道你是个什么玩意啊,less中的变量虽然表面上说是变量,但是实际上理解起来还是一个常量的。至于上面的这个问题该如何解决,可以根据具体的情况使用calc函数进行解决。

总结

上面简单的介绍了less中最重要的两个用法,结构化的嵌套和变量,但是less强大的地方不仅如此,less还有更多的强大的地方是三言两语说不清的。不过这些东西我大抵也不会,,,看来还是需要好好学习了。


一枚小菜鸡