Css

css扩展语言less和sass和scss相关

Posted by Qz on May 6, 2020

“Yeah It’s on. ”

less

https://less.bootcss.com/#%E6%A6%82%E8%A7%88

Less (Leaner Style Sheets 的缩写) 是一门向后兼容的 CSS 扩展语言。

less to css online

https://lesscss.org/less-preview/

functions

https://lesscss.org/functions/

extract

@list: apple, pear, coconut, orange;
value: extract(@list, 3);

// value: coconut;

~符号

https://cloud.tencent.com/developer/article/1976170

在.less文件中写:

.test_03{  
    width:calc(300px-30px);  
} 

将在相应的.css中得到如下效果:

    .test_03 {  
      width: calc(270px);  
    }  

也就是说此时的300px-30px已由编译软件计算得出。

而在.less文件中写(加上~):

    .test_03{  
        width:~'calc(300px-30px)';  
    }  

在相应的.css中会得到:

    .test_03 {  
      width: calc(300px-30px);  
    }  

也就是说让浏览器去计算300px-30px。

modifyVars

使用modifyVars可以在运行时修改LESS变量。当用新的变量值调用了这个函数时,LESS文件将会被重新编译,但不会被重新加载。一个基本的用法示例:

less.modifyVars({
    '@buttonFace': 'red',
    '@buttonText': '#fff'
});

webpack的less-loader

在module rules 中的less-loader 下配置options modifyVars。 实现 更改less 中的变量

{
                test: /\.less/, 
                use: [
                    {
                        loader: "style-loader"
                    },
                    {
                        loader: "css-loader"
                    },
                    {
                        loader: "less-loader",
                        options: {
                            "modifyVars":{ "@test": "#ffb200",  }
                        }
                    }
                ]
            },

在组件中

https://juejin.im/post/5ca41617f265da3092006155

import React from 'react';
import { loadScript } from '../../shared/utils';
import './index.less';
const colorCluters = ['red', 'blue', 'green'];

export default class ColorPicker extends React.Component {
    handleColorChange = color => {
        const changeColor = () => {
            window.less
                .modifyVars({  // 调用 `less.modifyVars` 方法来改变变量值
                    '@primary-color': color,
                    '@bg-color': '#2f54eb',
                })
                .then(() => {
                    console.log('修改成功');
                });
        };
        const lessUrl =
            'https://cdnjs.cloudflare.com/ajax/libs/less.js/2.7.2/less.min.js';

        if (this.lessLoaded) {
            changeColor();
        } else {
            window.less = {
                async: true,
            };

            loadScript(lessUrl).then(() => {
                this.lessLoaded = true;
                changeColor();
            });
        }
    };

    render() {
        return (
            <ul className="color-picker">
                {colorCluters.map(color => (
                    <li
                        style=
                        onClick={() => {
                            this.handleColorChange(color);
                        }}>
                        color
                    </li>
                ))}
            </ul>
        );
    }
}

这是在antd ui多颜色主题中实现的

问题

路径

Vue组件引入less文件,图片路径出现问题

.bg-image(@url) {
  background-image: url("@{url}@2x.png");
  @media (-webkit-min-device-pixel-ratio: 3),(min-device-pixel-ratio: 3) {
    background-image: url("@{url}@3x.png");
  }
}

出现问题 .bg-image(‘brand’)编译出来的图片的路径是less目录下

最终解决

.bg-image(@url){
 background-image:~"url(@{url}@2x.png)";
 @media (-webkit-min-device-pixel-ratio: 3),(min-device-pixel-ratio: 3){
   background-image:~"url(@{url}@3x.png)";
 }
}

字符串拼接

网页链接

@iconUrl: "/img";

// mixin
.c-icon(@bgImg) {
    background-image: url(@bgImg);
}

.bg {
    @someImgUrl: "/icon.png";

    // call mixin .c-icon();
    .c-icon("@{iconUrl}@{someImgUrl}");

    // or below
    // .c-icon("@{iconUrl}/icon.png");
}

字符串拼接中取字符串以@{varName}这种形式即可

@import引入路径中使用波浪号~

@import "~common/stylus/variable"

这个应该是webpack的路径解析相关的用法。 ~这里应该是指定的模块解析目录下进行匹配对应文件, webpack中可以通过resolve.modules字段修改模块目录。

在webpack.base.config.js下

  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      '@': resolve('src'),
      'common': resolve('src/common'),
      'components': resolve('src/components'),
      'api': resolve('src/api'),
      'base': resolve('src/base')
    }
  }

https://www.npmjs.com/package/less-loader

@import引入路径中使用波浪号~,stylus和less效果一致

循环

// less官方文档给的循环实例,其实实际上这就是递归调用
.loop(@counter) when (@counter > 0) {
    width: (10px + @counter);
    .loop((@counter - 1));
}

stylus

字符串拼接

在stylus中变量可以直接拼接成字符串

bg-image($url)
  background-image: url($url + "@2x.png")
  @media (-webkit-min-device-pixel-ratio: 3),(min-device-pixel-ratio: 3)
    background-image: url($url + "@3x.png")

scss和sass

区别:除了书写上面的区别,没有其他区别了

  • Sass 是以严格的缩进式语法规则来书写,不带大括号({})和分号(;)
  • Scss 的语法书写和我们的 CSS 语法书写方式非常类似

变量

举一个循环的例子:

  @for $i from 0 to 5 {
    // 这里一定要用 #{} 表达式  (虽然我也不知道为什么)  
    .wave#{$i}{
      background:lighten(red, $i * 10%);
      z-index:2 + $i;
      background-size:auto 100%;
      animation:w 1s $i * 0.2s forwards infinite;
    }
  }

` #{}`

#{}大括号中可以是任何有效的表达式,然后计算并返回这个表达式的值

p {
  $font-size: 12px;
  $line-height: 30px;
  font: $font-size/$line-height;
}

上面是一段简单的SCSS代码,声明两个变量$font-size和$line-height,并对它们赋值。

代码的本意是通过font设置当前元素的字体大小与行高,然而编译成CSS却是如下结果:

p {
  font: 0.4; 
}

很明显代码将斜杠当做了除法运算符,并计算出两个变量相除的结果。

虽然没有语法错误,但是却没有达到我们的预期效果,通过#{} 可以轻松解决上述问题,代码修改如下:

p {
  $font-size: 12px;
  $line-height: 30px;
  font: #{$font-size}/#{$line-height};
}

将两个变量分别放入大括号中,编程CSS代码如下:

p {
  font: 12px/30px; 
}

@at-root

https://sass-lang.com/documentation/at-rules/at-root/

https://www.sass.hk/skill/sass40.html

在我们CSS中有一种命名方式是BEM,如:

.block {} 
.block__element{} 
.block--modifier{}

试想我们在Sass中是否可以通过下面的方式来实现上面样式代码

#{&}_element{}

不仿我们来验证一下:

.block {
    color: red;

    #{&}__element {
        color:blue;
    }

    #{&}--modifier {
        color: orange;
    }
}

悲催的是,编译出来的CSS并不是我们想要的代码:

.block {
  color: red;
}
.block .block__element {
    color: blue;
}
.block .block--modifier {
    color: orange;
}

但在LESS和Stylus中,能很好的实现BEM类名的形式。此时在想,在Sass中有没有这样的功能呢?值得幸运的是,在Sass3.3中新增加了@at-root特性,能实现上面BEM的特性

.block {
  color: red;
  @at-root #{&}__element {
    color: blue;
  }
  @at-root #{&}--modifier {
    color: orange;
  }
}

前面的例子告诉我们@at-root是什么。通过他可以告诉Sass,你不想嵌套选择器。当使用&选择器时,就算你不想嵌套选择器,Sass也会自动嵌套。但往往很多时候,我们是不想要嵌套选择器,例如BEM。使用@at-root#{&}可以引用父(在Sass中总是引用父选择器)和插值,可以嵌套,做一些其他的事情

@at-root规范

内联选择器模式

SCSS

.foo {
    @at-root .bar {
        color:gray;
    }
}

CSS

.bar {
  color: gray; 
}

测试用例可以说明,@at-root的内联选择器模式,将不会让你的选择器发生任何的嵌套,直接移除了父选择。在来看一个嵌套深一点的用例:

SCSS

.foo {
    @at-root .bar {
        color: gray;

        @at-root button{
            color:red;

            @at-root span {
                color: orange;
            }
        }
    }
}

CSS

.bar {
  color: gray; 
}
button {
  color: red; 
}
span {
  color: orange; 
}

在SCSS中嵌套,使用@at-root内联选择器模式,编译出来的CSS无任何嵌套,让代码更加的简单。回到SCSS中的嵌套中,如果不使用@at-root内联选择器模式,将会按代码的层级关系一层一层往下嵌套。

@at-root&的结合

&在Sass中所起的作用,文章开头就简单的进行演示了。在@at-root中也同样可以配合&一起使用,下面我们同样来看几个用例:

SCSS

.foo {
    @at-root .bar & {
        color:gray;
    }
}

CSS

.bar .foo {
  color: gray; 
}

大家很容易发现,这个示例和不加@at-root的SCSS代码一样,可以编译出完全相同的代码:

.foo {
    .bar & {
        color:gray;
    }
}

同样的,&符和@at-root按下面的方式一起工作:

SCSS

.foo {
    @at-root & .bar {
        color:gray;
    }
}

.foo {
    @at-root &.bar {
        color:gray;
    }
}

CSS

.foo .bar {
  color: gray; 
}

.foo.bar {
  color: gray; 
}

同样如此,这种方式与不加@at-root方式,运行的效果是一样的:

.foo {
    & .bar {
        color:gray;
    }
}

.foo {
    &.bar {
        color:gray;
    }
}

如此说明,在Sass中同时使用@at-root&起到的作用是一样的,换句话说,这样并没有带来新的特性,而且在整个开发中还带来了额外的工作量。

@at-root#{&}结合

Sass有脚本模式#{},他和&不同之处是,&只用作选择器,它只能出现在一个复合的开始选择器,类似于一个类型选择器,如a或者h1。但#{}他表示的是一个插值,它可以用在任何地方。同样的,当@at-root#{&}一起使用时,可以给我们的开发带来极大的方便与优势。例如:

SCSS

.foo {
    @at-root .bar #{&} {
        color: gray;
    }
}

.foo {
    @at-root #{&} .bar {
        color:gray;
    }
}
.foo {
    @at-root #{&}.bar {
        color:gray;
    }
}

CSS

.bar .foo {
  color: gray; 
}

.foo .bar {
  color: gray; 
}

.foo.bar {
  color: gray; 
}

bem时使用@at-root就能尽显其英雄本色

@content

@content这个是不是个类似占位用的,代表里面是自定义的的

    @-webkit-keyframes #{$name} {
        @content;
    }

@debug

The @debug directive prints the value of a SassScript expression to the standard error output stream. It’s useful for debugging Sass files that have complicated SassScript going on.

@debug指令将SassScript表达式的值打印到标准的错误输出流。这对于调试具有复杂SassScript的Sass文件非常有用

@debug 10em + 12em;

编译为

Line 1 DEBUG: 22em

@function

Sass 支持自定义函数,并能在任何属性值或 Sass script 中使用:

$grid-width: 40px;
$gutter-width: 10px;

@function grid-width($n) {
  @return $n * $grid-width + ($n - 1) * $gutter-width;
}

#sidebar { width: grid-width(5); }

编译为

#sidebar {
  width: 240px; 
}

@each

https://sass-lang.com/documentation/at-rules/control/each

@each $var in <list or map>
  • $var: 它表示变量的名称。 @each规则将$var设置到列表中的每个项目,并使用值$var输出样式。
  • ` `: 这些是SassScript表达式,将返回列表或映射。

例子:

@each $color in red, green, yellow, blue {
  .p_#{$color} {
    background-color: #{$color};
  }
}

转义后:

.p_red {
  background-color: red; }

.p_green {
  background-color: green; }

.p_yellow {
  background-color: yellow; }

.p_blue {
  background-color: blue; }

$icons: ("eye": "\f112", "start": "\f12e", "stop": "\f12f");

@each $name, $glyph in $icons {
  .icon-#{$name}:before {
    display: inline-block;
    font-family: "Icon Font";
    content: $glyph;
  }
}

转义后

@charset "UTF-8";
.icon-eye:before {
  display: inline-block;
  font-family: "Icon Font";
  content: "";
}

.icon-start:before {
  display: inline-block;
  font-family: "Icon Font";
  content: "";
}

.icon-stop:before {
  display: inline-block;
  font-family: "Icon Font";
  content: "";
}

!global

此标志可以改变一个局部变量的作用域范围。

@mixin button-style {
  $btn-bg-color: lightblue;
  color: $btn-bg-color;
}
button {
  @include button-style;
}
.wrap {
  background: $btn-bg-color;
}

上面的代码会报错如下:

Error: Undefined variable: "$btn-bg-color".

很明显,由于变量是声明在@mixin混合器之内,所以无法在.wrap选择器中使用。

代码修改如下:

@mixin button-style {
  $btn-bg-color: lightblue !global;
  color: $btn-bg-color;
}
button {
  @include button-style;
}
.wrap {
  background: $btn-bg-color;
}

上面的代码编译成css代码如下:

button {
  color: lightblue; 
}
.wrap {
  background: lightblue; 
}

再来看一段代码实例:

@mixin button-style {
  $btn-bg-color: lightblue !global;
  color: $btn-bg-color;
}
.wrap {
  background: $btn-bg-color;
}

上面的代码尽管添加了!global,但是依然会报错。

这是因为@mixin混合器如果只声明没有引用的话,那么它内部的代码是没有生效的。

!default

https://blog.csdn.net/weixin_44198965/article/details/101169272

如果分配给变量的值后面添加了 !default 标志 ,这意味着该变量如果已经赋值,那么它不会被重新赋值,但是,如果它尚未赋值,那么它会被赋予新的给定值。

如果在此之前变量已经赋值,那就不使用默认值,如果没有赋值,则使用默认值。


例子:

如果变量之前没有赋值,则使用默认值:

/* 如果之前没有赋值,则使用默认值 */
$const: "hello" !default;

div{
    const: $const;
}

编译为CSS:

div {
  const: "hello"; 
}

可以看到,const 的值为 “hello”,正确。


如果在此之前已经赋值,那就不再使用默认值:

/* 如果之前已经赋值,则不再使用默认值 */
$const: "Hi";
$const: "hello" !default;

div{
    const: $const;
}

编译为CSS:

div {
  const: "Hi"; 
}

可以看到,const 的值为 “Hi” ,正确。

deep

scss中使用/deep/深度选择器报错的解决办法

/deep/ 是什麼

一般用于改第三方组件的样式

Vue为了让scoped有效会在编译的时候为每个元素添加[data-v-xxx],当你引入第三方组件的时候,这个[data-v-xxx]只会加在组件的第一层,内层元素不会添加

// 对于element的checkbox组件
.el-checkbox-group {
    margin: 10px;
    .el-checkbox.is-checked {
        .el-checkbox__label {
            color: #606266;
        }
    }
}

将编译成:

.el-checkbox-group[data-v-6de9570b] {
    margin: 10px;
}

.el-checkbox-group[data-v-6de9570b] .el-checkbox.is-checked[data-v-xxx] .el-checkbox__label[data-v-xxx] {
    color: #606266;
}


.el-checkbox-group {
    margin: 10px;
    /deep/ .el-checkbox.is-checked {
        .el-checkbox__label {
            color: #606266;
        }
    }
}

将编译成

.el-checkbox-group[data-v-6de9570b] {
    margin: 10px;
}
.el-checkbox-group[data-v-6de9570b] .el-checkbox.is-checked .el-checkbox__label {
    color: #606266;
}

string

https://www.runoob.com/sass/sass-string-func.html

  • quote(string)
  • str-index(string, substring)
  • str-insert(string, insert, index)
  • str-length(string)
  • str-slice(string, start, end)
  • to-lower-case(string)
  • to-upper-case(string)
  • unique-id()
  • unquote(string)

Maps

Maps代表一个键和值对集合,其中键用于查找值。他们可以很容易地将值收集到命名组中,并且可以动态地访问这些组。在CSS中你找不到和他们类似的值,虽然他们的语法类似于媒体查询表达式:

$map: (key1: value1, key2: value2, key3: value3);

和列表(Lists)不同,Maps必须始终使用括号括起来,并且必须用逗号分隔。Maps中的键和值可以是任意的SassScript对象。一个Maps可能只有一个值与给定的键关联(尽管该值可以是一个列表)。一个给定的值可能与许多键关联。

和列表(Lists)类似,Maps的主要操作使用的是 SassScript 函数map-get函数用于查找map中的值,map-merge函数用于添加值到map中的值, @each 指令可以用来为 map 中的每个键值对添加样式。map中键值对的顺序和map创建时始终相同。

Maps不能转换为纯CSS。作为变量的值或参数传递给CSS函数将会导致错误。使用inspect($value) 函数以产生输出字符串,这对于调试 maps 非常有用。

inspect

Maps不能转换为纯CSS。作为变量的值或参数传递给CSS函数将会导致错误。使用inspect($value) 函数以产生输出字符串,这对于调试 maps 非常有用。

bem

b 选择块

添加前缀,包裹原来样式

/* BEM
$namespace: 'el';
 -------------------------- */
@mixin b($block) {
  $B: $namespace+'-'+$block !global;

  .#{$B} {
    @content;
  }
}

e 选择快

// $element-separator: '__';

@mixin e($element) {
  $E: $element !global;
  $selector: &;
  $currentSelector: "";
  @each $unit in $element {
    $currentSelector: #{$currentSelector + "." + $B + $element-separator + $unit + ","};
  }

  @if hitAllSpecialNestRule($selector) {
    @at-root {
      #{$selector} {
        #{$currentSelector} {
          @content;
        }
      }
    }
  } @else {
    @at-root {
      #{$currentSelector} {
        @content;
      }
    }
  }
}

// ---------------------------------------------




@function hitAllSpecialNestRule($selector) {
  @return containsModifier($selector) or containWhenFlag($selector) or containPseudoClass($selector);
}




@function containsModifier($selector) {
  $selector: selectorToString($selector);

  @if str-index($selector, $modifier-separator) {
    @return true;
  } @else {
    @return false;
  }
}

@function containWhenFlag($selector) {
  $selector: selectorToString($selector);

  @if str-index($selector, '.' + $state-prefix) {
    @return true
  } @else {
    @return false
  }
}

@function containPseudoClass($selector) {
  $selector: selectorToString($selector);

  @if str-index($selector, ':') {
    @return true
  } @else {
    @return false
  }
}

// BEM support Func
// ------------------------------------------


@function selectorToString($selector) {
  $selector: inspect($selector);
  $selector: str-slice($selector, 2, -2);
  @return $selector;
}

m 选择块

//  $modifier-separator : --
@mixin m($modifier) {
  $selector: &;
  $currentSelector: "";
  @each $unit in $modifier {
    $currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ","};
  }

  @at-root {
    #{$currentSelector} {
      @content;
    }
  }
}

状态命中

// $state-prefix: 'is-';

@mixin when($state) {
  // 使用@at-root提至最外层
  @at-root {
    &.#{$state-prefix + $state} {
      @content;
    }
  }
}

使用

.foo {
  color: red;

  @include when(active) {
    color: blue;
  }
}

转化成css

.foo {
  color: red;
}

.foo.is-active {
  color: blue;
}