Less学习笔记

Posted by Youzi Blog on April 29, 2020

Less——CSS 预处理器

为什么用Less,原因之一就是CSS太繁琐了,写起来没章法,没有变量,导致改动起来要修改好多地方;

同款预处理器还有:Sass || Stylus;预处理器其实是把Less这些代码编译成CSS,看起来和TS / JS的关系挺像的。

使用Less

由于Less不能直接被浏览器引入使用,所以要介绍下怎么用;

  • 编译成CSS后引入使用;

    1. 手动编译:npm i -g less,全局安装Less,使用命令lessc [option option=parameter ...] <source> [destination],直接将Less文件编译成CSS文件;
    2. 编辑器的插件:easy Less等插件,保存时自动编译;
    3. 项目中引入Less && Less-loader,在webpack里配置
    1
    2
    3
    4
    
    {
      test: /\.less$/,
      loader: "style-loader!css-loader!less-loader",
    }
    
  • 全局引入less.js,使得Less文件在浏览器中变得可用;

    1
    2
    
    <link rel="stylesheet/less" type="text/css" href="styles.less" />
    <script src="less.js" type="text/javascript"></script>
    

语法详解

变量

// less
@val: white;
@selector: #id;
@prop: color;
@url: '../img';
@backImg: background-image;

@mixin: {
  margin-left: 10px;
  font-size: 16px;
};

@num: 200px;
@numb: @num - 100;

@{selector} {
  @{prop}: @val;
  @{backImg}: url('@{url}/index.jpg');
  @mixin ();
  width: @num - 10;
  height: @numb * 2;
}

// css
#id {
  color: white;
  background-image: url('../img/index.jpg');
  margin-left: 10px;
  font-size: 16px;
  width: 190px;
  height: 200px;
}

变量作用域问题:就近原则,并且使用变量可以在变量定义之前;

你甚至可以用变量来定义另一个变量;

@var2: red;
@var: 'var2';
div {
  color: @@var;
}
/* 生成的 CSS */
div {
  color: red;
}

嵌套

在嵌套中,&代表了上一层的选择器;

// 编译前
@{selector} {
  &::after {
    content: '?';
  }
  p {
    text-decoration: double;
  }
  &-container {
    color: red;

    &__content {
      background-color: blue;
    }
  }
}
// 编译后
#id::after {
  content: '?';
}
#id-container {
  color: red;
}
#id-container__content {
  background-color: blue;
}
#id p {
  text-decoration: double;
}

媒体查询

// less
@{selector} {
  @media screen {
    @media (max-width: 768px) {
      @mixin ();
    }
  }

  @media tv {
    @{prop}: red;
  }
}
// css
@media screen and (max-width: 768px) {
  #id {
    margin-left: 10px;
    font-size: 16px;
  }
}
@media tv {
  #id {
    color: red;
  }
}

混入

其实刚刚已经用了,混入用的函数可传参数,并且和 ES6 一样可以有默认参数,@arguments则表示所有参数,需要注意的是传入的参数需要带有单位;混入还有个要注意的点,在定义混入时,如果不加(),会在CSS里面渲染出混入的选择器和样式,如果加了(),就不会渲染;

.container {
  color: red;
}
#container {
  .container();
  // 或者
  .container;
}

// 传参
.container(@color: red, @param1: 10px) {
  color: @color;
  width: @param1;
  border: @arguments;
}

@{selector} {
  .container();
}
.div {
  .container(blue, 20px);
}
// css
#id {
  color: red;
  width: 10px;
  border: red 10px;
}
.div {
  color: blue;
  width: 20px;
  border: blue 20px;
}
// less
.mixin() {
  color: red;
}
@{selector} {
  .mixin();
}
// css
#id {
  color: red;
}

混入的方法也可以用编译时多态来定义;

.func(top,@param1) {
  top: @param1;
}
.func(bottom, @param1) {
  bottom: @param1;
}
.func(@_,@param1) {
  left: 0;
  right: 0;
}
@{selector} {
  .func(top, 10px);
}
// css
#id {
  top: 10px;
  left: 0;
  right: 0;
}

这种模式会匹配第一个参数,找到匹配度最高的那个函数,如果匹配度相同就全部选择,并且样式会覆盖;如果匹配的参数是个变量,会直接匹配。

上面这个函数可以看到,匹配了第一个参数是top && @_的两个函数,都混入了选择器中。

混入方法的命名空间

.fun1() {
  .fun2(@para2: 20px) {
    width: @para2;
    height: @para2;

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

@{selector} {
  .fun1 > .fun2(10px);
}
// css
#id {
  width: 10px;
  height: 10px;
}
#id:hover {
  background-color: white;
}

这是中文文档的一个例子,可以用选择器>或者直接.fun1.fun2调用,这其实是直接后代选择器,且父选择器不能加括号;而且命名空间函数必须要先引入命名空间,才可以调用里面的函数;另外还可以看到,内部方法可以使用外部方法的参数。

方法的条件筛选

Less没有if else语法,但是有when语法;

.fun3 {
  .fun1(@para1, @para2) when (@para1 > 10px) and (@para2 =#eee) {
    width: @para1;
    color: @para2;
  }

  .fun2(@para1, @para2: 1px) when not (@para1 > 10px) {
    width: @para1;
  }

  .fun3(@para1, @para2) when (@para1 > 10px),
  (@para2 > 20px) {
    width: @para1 + @para2;
  }
}

@{selector} {
  .fun3 > .fun3(20px, 10px);

  .div {
    .fun3 > .fun1(20px, #eee);
  }

  .text {
    .fun3 > .fun2(5px);
  }
}
// css
#id {
  width: 30px;
}
#id .div {
  width: 20px;
  color: #eee;
}
#id .text {
  width: 5px;
}

数量不定的参数

Less还可以使用扩展运算符

.fun4(...) {
  border: @arguments;
}

@{selector} {
  .fun4(1px, solid, red);
}
// css
#id {
  border: 1px solid red;
}

需要使用!important时,直接在方法后面加上!important即可;

循环方法

Less虽然没有提供for语法,但是可以借用递归来实现;

.for-fun(@n, @i: 1) when (@i =< @n) {
  &:nth-of-type(@{i}) {
    animation-delay: (0.2s * @i);
  }

  .for-fun(@n, (@i + 1));
}

li {
  animation: move 1s ease infinite;
  .for-fun(5);
}
// css
li {
  animation: move 1s ease infinite;
}
li:nth-of-type(1) {
  animation-delay: 0.2s;
}
li:nth-of-type(2) {
  animation-delay: 0.4s;
}
li:nth-of-type(3) {
  animation-delay: 0.6s;
}
li:nth-of-type(4) {
  animation-delay: 0.8s;
}
li:nth-of-type(5) {
  animation-delay: 1s;
}

属性拼接

+_: space; +: comma

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

方法返回值的实例

.fun5(@p1, @p2) {
  @return: (@p1 + @p2);
}

.div {
  .fun5(10px, 20px);
  width: @return;
}
// css
.div {
  width: 30px;
}

需要注意的点是返回值需要用()包裹,并且+和变量名之间需要有空格,否则会被识别为字符串;

继承

extend语法,是作为Less中的一个伪类存在的;和混入方法的区别就是,继承在编译时会把被继承的属性写在一起,减少代码冗余,而混入方法会分开,看个例子吧;

/* extend */
#main {
  width: 200px;
}
#main {
  &:after {
    content: 'Less is good!';
  }
}
#wrap:extend(#main aLess) {
}
/* css */
#main,
#wrap {
  width: 200px;
}
#main:after,
#wrap:after {
  content: 'Less is good!';
}

/* mixin */
.mix {
  width: 100px;
  &::before {
    content: '123123';
  }
}
#mixin {
  .mix;
}
/* css */
.mix {
  width: 100px;
}
.mix::before {
  content: '123123';
}
#mixin {
  width: 100px;
}
#mixin::before {
  content: '123123';
}

还是比较明显的区别,用继承语法会把相同属性的几个选择器写在一起,而混入语法就不会;

另外还要提的一个点就是aLess这个关键字,不用aLess的话就只会继承main,而不会继承:after部分;

/* not aLess */
.extend {
  height: 100px;
  .inner {
    height: 20px;
  }
}
.div:extend(.extend) {
  width: 100px;
}
/* css */
.extend,
.div {
  height: 100px;
}
.extend .inner {
  height: 20px;
}
.div {
  width: 100px;
}

/* aLess */
.extend {
  height: 100px;
  .inner {
    height: 20px;
  }
}
.div2:extend(.extend aLess) {
  width: 200px;
}
/* css */
.extend,
.div2 {
  height: 100px;
}
.extend .inner,
.div2 .inner {
  height: 20px;
}
.div2 {
  width: 200px;
}

区别还是比较明显的,就是有没有继承.inner这个选择器的区别了。

其他注意点

  • 可以选择多个继承的选择器,写法也有两种,一种是div:extend(.extend):extend(.another),还有一种是div:extend(.extend, another)
  • 也可以只选择某个选择器的子选择器,像上面那个例子就可以选择.div:extend(.extend .inner)
  • 继承语法需要放在选择器最后,不能在继承语法后面再写选择器。

导入

import语法,@import (options) "filename"

  • CSS的区别就是无所谓在哪引入,类似JS的变量提升,而在CSS里需要在使用前引入;
  • 还可以省略后缀,大部分情况都会把文件当做是Less文件处理,除非特别设置了一些options

options共有以下几种,可以组合使用的;

  • reference: 单纯引入,不会编译到最终输出的CSS文件里;
  • inline: 会包含在最终的输出文件里,但不会编译处理;
  • less: 无论文件后缀名是什么,都会当成Less文件处理;
  • css: 当做CSS文件处理;
  • once: 默认行为,文件只会被导入一次,即使写了两次导入语句,第二句会被忽略;
  • multiple: 允许多次导入,会多次编译;
  • optional: 即使当导入的文件没有找到,也会执行编译;

函数

其实前面也提到过我们可以单独引入less.js来在浏览器环境下直接用Less语法,所以其实我们在Less中可以用一些JS的函数;

丢,函数太多了,看文档

杂项

  • Less中有两种注释,单行和多行,其实在CSS里面只能识别多行,所以单行不会被编译在CSS文件里;
  • 有时候需要取消编译某一个属性,可以这样写prop: ~'calc(300px - 30px)',注意使用~并且把不需要编译的值用''包裹;
  • 还可以在Less文件里写JS,目前搜到的方法是要引入less.js,但是目前尝试下来是不行的;
  • 另外有一点要注意,使用Less.js引入时,如果使用静态文件直接访问html文件,会提示跨域错误,所以要起本地服务器的方式,IIS node都可以。