less/sass和Vue3的新特性css变量

1,293 阅读8分钟

css 预处理器

1. css 使用的缺点

1)css语法不够强大,因为无法嵌套导致有很多重复的选择器;

2)没有变量和合理的样式复用机制,导致逻辑上相关的属性值只能以字面量的形式重复输出,难以维护。

2. css预处理器的定义

为css增加编程特性的拓展语言,可以使用变量、简单逻辑判断、函数等基本编程技巧;

用一种专门的编程语言,进行 Web 页面样式设计,再通过编译器转化为正常的 CSS 文件,以供项目使用。

说明:css预处理器编译输出还是标准的css样式。

3. 预处理器带来的好处

1)css代码更加整洁,更易维护,代码量更少;

2)修改更快,基础颜色使用变量,一处动处处动;

3)常用代码使用代码块,节省大量代码;

4)css嵌套减少了大量的重复选择器,避免一些低级;

5)变量、混入大大提升了样式的复用性;

6)额外的工具类似颜色函数 (lighten,darken,transparentize等等),mixins,loops,这些方法使css更像一个真正的编程语言,让开发者能够有能力生成更加复杂的css样式。

sass 和 less 预处理器

Sass、Less、Stylus都是动态的样式语言,是css预处理器,css上的一种抽象层。他们是一种特殊的语法/语言而编译成css。

Sass是一种动态样式语言,Sass语法属于缩排语法,比css比多出好些功能(如变量、嵌套、运算,混入(Mixin)、继承、颜色处理,函数等),更容易阅读。

Sass官网地址:www.sass.hk/ Sass与Scss是什么关系? Sass的缩排语法,对于写惯css前端的web开发者来说很不直观,也不能将css代码加入到Sass里面,因此sass语法进行了改良,Sass 3就变成了Scss(sassy css)。与原来的语法兼容,只是用{}取代了原来的缩进。

Less是一种动态样式语言. 对CSS赋予了动态语言的特性,如变量、继承、运算、函数。Less 既可以在客户端上运行 (支持IE 6+, Webkit, Firefox),也可在服务端运行。 LESS的官网: less.bootcss.com/

Sass与Less区别

  • 1、实现方式 1)Less是基于JavaScript,是在客户端处理的。

2)Sass是基于Ruby的,是在服务器端处理的。

  • 2、变量 1)sass 是以开头定义的变量,如:开头定义的变量,如:开头定义的变量,如:mainColor: #963;

2)less 是以@开头定义的变量,如 @mainColor: #963;

Sass与Less,选择Sass

为什么选择使用Sass而不是Less?

1、Sass在市面上有一些成熟的框架,比如说Compass,而且有很多框架也在使用Sass,比如说Foundation。

2、就国外讨论的热度来说,Sass绝对优于LESS。

3、就学习教程来说,Sass的教程要优于LESS。在国内LESS集中的教程是LESS中文官网,而Sass的中文教程,慢慢在国内也较为普遍。

4、Sass也是成熟的CSS预处理器之一,而且有一个稳定,强大的团队在维护。

5、同时还有Scss对sass语法进行了改良,Sass 3就变成了Scss(sassy css)。与原来的语法兼容,只是用{}取代了原来的缩进。

6、bootstrap(Web框架)最新推出的版本4,使用的就是Sass。

sass 预处理器使用

1. 变量

sass使用$符号来标识变量(老版本的sass使用!来标识变量。

$highlight-color: #F90;

与CSS属性不同,变量可以在css规则块定义之外存在。当变量定义在css规则块内,那么该变量只能在此规则块内使用。

$nav-color: #F90;
nav {
  $width: 100px;
  width: $width;
  color: $nav-color;
}

//编译后

nav {
  width: 100px;
  color: #F90;
}

在声明变量时,变量值也可以引用其他变量。

$height : 10px;
$width: $height + 10px;

nav {
    width: $width;
}

2. 嵌套(Nesting)

子组合选择器和同层组合选择器:>、+和~

这三个组合选择器必须和其他选择器配合使用,以指定浏览器仅选择某种特定上下文中的元素。 这些组合选择器可以毫不费力地应用到sass的规则嵌套中。可以把它们放在外层选择器后边,或里层选择器前边:

article {
  ~ article { border-top: 1px dashed #ccc }
  > section { background: #eee }
  dl > {
    dt { color: #333 }
    dd { color: #555 }
  }
  nav + & { margin-top: 0 }
}

sass会如你所愿地将这些嵌套规则一一解开组合在一起:

article ~ article { border-top: 1px dashed #ccc }
article > footer { background: #eee }
article dl > dt { color: #333 }
article dl > dd { color: #555 }
nav + article { margin-top: 0 }

属性嵌套

属性嵌套指的是有些属性拥有同一个开始单词,如border-width,border-color都是以border开头。

nav {
    border: {
        style: solid;
        width: 1px;
        color: #ccc; 
      }
}

// 编译成功后的css为:

nav {border1px soild #ccc;}

选择器嵌套

选择器嵌套指的是在一个选择器中嵌套另一个选择器来实现继承,从而增强了文件的结构性和可读性。在选择器嵌套中,可以使用&表示父元素选择器。

#content {
    article {
        h1 { color: #333 }
        p { margin-bottom: 1.4em }
    }
    aside { background-color: #EEE } 
}

// 编译成功后的css为:

#content article h1 { color: #333 } 
#content article p { margin-bottom: 1.4em } 
#content aside { background-color: #EEE }

Mixins (混入)

sass样式中声明Mixins时需要使用“@mixin”,然后后面紧跟Mixins的名,他也可以定义参数,同时可以给这个参数设置一个默认值,但参数名是使用“$”符号开始,而且和参数值之间需要使用冒号(:)分开。可以重用的代码块。 Sass 混入语法:

/* Sass mixin error with (optional) argument $borderWidth which defaults to 2px if not specified */
@mixin error($borderWidth: 2px) {
    border: $borderWidth solid #F00;
    color: #F00; 
}

 .generic-error {
    padding: 20px;
    margin: 4px;
    @include error(); /* Applies styles from mixin error */
} 

.login-error {
    left: 12px;
    position: absolute;
    top: 20px;
    @include error(5px); 
    /* Applies styles from mixin error with argument $borderWidth equal to 5px*/
}

混合关键词传参 //编译前

@mixin button-variant($color, $background, $border) {
  color: $color;
  border: 1px solid $border;
  background-color: $background;
}
.block{
  @include button-variant( $background:#fff,$color:red, $border:red)
}

超出省略号 (与项目结合)

// ****** 超出省略号
@mixin ellipsis($line) {
  display: -webkit-box;
  -webkit-box-orient:vertical;
  overflow: hidden;
  -webkit-line-clamp: $line;
}

.tit-name {
  @include ellipsis(1);
  color: #333;
}

继承

sass的继承:@extend

//sass style 
h1{
border: 4px solid #ff9aa9;
}
.speaker{
  @extend h1;
  border-width: 2px;
}

//css style
//-------------------------------
h1,.speaker{
	border: 4px solid #ff9aa9;
}
.speaker{
	border-width: 2px;
}

函数

最好的例子应该是 REM 布局中的 px2rem 函数了。(移动端开发)

@function px2rem ($px) {
  $rem : 37.5px;
  @return ($px / $rem) + rem;
}

.hello {
	width: px2rem (100px);
	height: px2rem (100px);
	&.b {
    width: px2rem ( 50px);
    height: px2rem (50px);
	}
}

导入SASS文件;

css有一个特别不常用的特性,即@import规则,它允许在一个css文件中导入其他css文件。然而,后果是只有执行到@import时,浏览器才会去下载其他css文件,这导致页面加载起来特别慢。

sass也有一个@import规则,但不同的是,sass的@import规则在生成css文件时就把相关文件导入进来。这意味着所有相关的样式被归纳到了同一个css文件中,而无需发起额外的下载请求。

使用sass的@import规则并不需要指明被导入文件的全名。你可以省略.sass或.scss文件后缀

使用SASS部分文件

当通过@import把sass样式分散到多个文件时,你通常只想生成少数几个css文件。那些专门为@import命令而编写的sass文件,并不需要生成对应的独立css文件,这样的sass文件称为局部文件。

sass局部文件的文件名以下划线开头。这样,sass就不会在编译时单独编译这个文件输出css

默认变量值;

!default用于变量,含义是:如果这个变量被声明赋值了,那就用它声明的值,否则就用这个默认值。

/* $fancybox-width: 400px !default;

.fancybox {
width: $fancybox-width;
} */

在上例中,如果用户在导入你的sass局部文件之前声明了一个 $fancybox-width 变量,那么你的局部文件中对 $fancybox-width 赋值400px的操作就无效。如果用户没有做这样的声明,则 $fancybox-width 将默认为400px

less 预处理器使用

1. 变量

Less 的变量名使用 @ 符号开始: 例如:

@mainColor: #0982c1;
@siteWidth: 1024px; 
@borderStyle: dotted; 
body {
    color: @mainColor;
    border: 1px @borderStyle @mainColor;
    max-width: @siteWidth; 
}

// 编译成功后的css为:

body {
    color: #0982c1;
    border: 1px dotted #0982c1;
    max-width:  1024px;
}

在声明变量时,变量值也可以引用其他变量。


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

#header {
  width: @width;
  height: @height;
}
编译为:
#header {
  width: 10px;
  height: 20px;
}

2. 嵌套

嵌套(Nesting) Less 提供了使用嵌套(nesting)代替层叠或与层叠结合使用的能力。假设我们有以下 CSS 代码:

#header {
  color: black;
}
#header .navigation {
  font-size: 12px;
}
#header .logo {
  width: 300px;
}
用 Less 语言我们可以这样书写代码:
#header {
  color: black;
  .navigation {
    font-size: 12px;
  }
  .logo {
    width: 300px;
  }
}

用 Less 书写的代码更加简洁,并且模仿了 HTML 的组织结构。

混合(Mixins)

混合(Mixin)是一种将一组属性从一个规则集包含(或混入)到另一个规则集的方法。假设我们定义了一个类(class)如下:

.bordered {
  border-top: dotted 1px black;
  border-bottom: solid 2px black;
}
如果希望在其它规则集中使用这些属性,只需像下面这样输入所需属性的类(class)名称即可
#menu a {
  color: #111;
  .bordered();
}

.post a {
  color: red;
  .bordered();
}
/* LESS mixin error with (optional) argument @borderWidth which defaults to 2px if not specified */ 
.error(@borderWidth: 2px) {
    border: @borderWidth solid #F00;
    color: #F00; 
} 

.generic-error {
    padding: 20px;
    margin: 4px;
    .error(); /* Applies styles from mixin error */ 
} 

.login-error {
    left: 12px;
    position: absolute;
    top: 20px;
    .error(5px); 
/* Applies styles from mixin error with argument @borderWidth equal to 5px */
}

// 编译成功后的css为:

.generic-error {
    padding: 20px;
    margin: 4px;
    border: 2px solid #f00;
    color: #f00; 
} 
.login-error {
    left: 12px;
    position: absolute;
    top: 20px;
    border: 5px solid #f00;
    color: #f00 ; 
}

继承

less的继承:类似于mixins

//less style
.block {
    margin: 10px 5px;
    padding: 2px; 
} 

p {
  .block;/*继承.block选择器下所有样式*/
  border: 1px solid #eee; 
} 
ul,ol {  
  .block; /*继承.block选择器下所有样式*/
  color: #333;
  text-transform: uppercase;
}

函数

Less 内置了多种函数用于转换颜色、处理字符串、算术运算等。这些函数在Less 函数手册中有详细介绍。

函数的用法非常简单。下面这个例子将介绍如何利用 percentage 函数将 0.5 转换为 50%,将颜色饱和度增加 5%,以及颜色亮度降低 25% 并且色相值增加 8 等用法:

@base: #f04615;
@width: 0.5;

.class {
  width: percentage(@width); // returns `50%`
  color: saturate(@base, 5%);
  background-color: spin(lighten(@base, 25%), 8);
}
@function px2rem ($px) {
  $rem : 37.5px;
  @return ($px / $rem) + rem;
}

.hello {
	width: px2rem (100px);
	height: px2rem (100px);
	&.b {
    width: px2rem ( 50px);
    height: px2rem (50px);
	}
}

Vue3 新特性 css 变量

为什么 Vue3 选择了 CSS 变量

Vue 3 新增了一条实验性的功能——「单文件组件状态驱动的 CSS 变量」 image

之前的css 变量

CSS 变量又称为 CSS 自定义属性,它包含的值可以在整个文档中重复使用。由自定义属性标记设定值(比如: --main-color: black;),由 var() 函数来获取值(比如: color:  var(--main-color);)

为什么选择两根连词线(--)表示? 因为变量 ? 被 Sass 用掉了,@ 被 Less 用掉了。为了不产生冲突,官方的 CSS 变量就改用两根连词线了

给个例子

<div class="parent">
  I am Parent
  <div class="child">
    I am Child
  </div>
</div>
.parent {
  /*  变量的作用域就是它所在的选择器的有效范围,所以.parent 读取不到 child 中的变量  */
  color: var(--body-child);
  /*  定义变量  */
  --parent-color: blue;
}
.child {
  /*  通过 var 读取变量  */
  color: var(--parent-color);
  --child-color: green;
}

我们现在 .parent 中定义变量 --parent-color: blue;,在 .child 中使用 color: var(--parent-color);

需要注意的是,变量的作用域就是它所在的选择器的有效范围,比如 .child 中定义的 --child-color: green;, 在 .parent 读取不到的,只针对 .child 元素下的元素有效

如果希望能够在 HTML 文档中都能访问到,则可以定义在类 :root 中

除了基础的使用,还有以下几点需要注意

  • CSS 变量的命名是对大小写敏感的,也就是 --myColor 和 --mycolor 是不一样的
  • var() 参数可以使用第二个参数设置默认值,当该变量无效的时候,就会使用这个默认值
  • CSS 变量提供了 JavaScript 与 CSS 通信的一种途径,在 JS 中我们可以操作 CSS,跟操作普通的 CSS 属性是一样
// 获取一个 Dom 节点上的 CSS 变量
element.style.getPropertyValue("--my-var");

// 获取任意 Dom 节点上的 CSS 变量
getComputedStyle(element).getPropertyValue("--my-var");

// 修改一个 Dom 节点上的 CSS 变量
element.style.setProperty("--my-var", jsVar + 4);

Vue2 中使用css 变量

说实话很少用到我们应该直接用less sass

上面说了,CSS 变量并不是什么某个框架的产物,而是原生 CSS 的标准规范。那么在 Vue 2 中直接使用 CSS 变量肯定可以的,并没什么约束。

关键是我们怎么让 Vue 组件中的状态同步到 CSS 变量中,其实也很简单,通过 Style 绑定 即可。

<template>
  <!-- 如果要该组件都可以使用,则必须放置在根元素下 -->
  <div class="hello" :style="styleVar">
    <div class="child-1">I am Child 1</div>
    <div class="child-2">I am Child 2</div>
    <div @click="onClick">Change Red TO Blue</div>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  data() {
    return {
      styleVar: {
        "--colorBlue": "blue",
        "--colorRed": "red",
        "--fontSize": "30px",
        "--fontSizeTest": "30px",
      },
    };
  },
  methods: {
    onClick() {
      this.styleVar["--fontSizeTest"] = "40px";
    },
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.child-1 {
  color: var(--colorBlue);
  font-size: var(--fontSize);
}
.child-2 {
  color: var(--colorRed);
  font-size: var(--fontSizeTest);
}
</style>

我们只需要在组件的根元素中设置 :style="styleVar"(如果要该组件都可以使用,则必须放置在根元素下),就可以在 Vue 2.x 中实现组件中的状态和 CSS 值的绑定,而且这种绑定关系是响应式的,比如我定义一个方法,改变 font-size 的值,是可以实时更新的 image

:style VS CSS 变量

这里有个问题,现有的 Vue 可以通过 :style 的方式定义去动态绑定 CSS,比如我可以直接在上面的 .child-1中做如下绑定,效果跟上面是一致的。

<div class="child-1" :style="{ color: 'blue', fontSize: '30px' }">
  I am Child 1
</div>

那我为什么还要使用 CSS 变量?这样大费周章是否真有意义?

我总结有如下两个原因:

原因一: 复杂的网站都会有大量的 CSS 代码,通常也会有许多重复的值。当组件中的一个状态被几十个地方用到时,那么你可能需要绑定很多个 :style。一来代码会显得可读性不强,二来性能上应该是比原生的要差,毕竟要将更改经过 Vue 的指令绑定到每一个元素上(这一点暂未验证) 通过 CSS 变量,就可以直接通过在组件的根元素设置变量,在组件内部 中直接使用即可

原因二:伪元素的使用 如果直接使用 :style 我们无法设置伪元素的样式,而 CSS 变量就可以

p::first-line {
  color: var(--theme-secondary-color);
}

在 Vue 3 中使用 CSS 变量

新增 vars 绑定

<template>
  <div class="text">hello</div>
</template>

<script>
export default {
  data() {
    return {
      color: "red",
      border: '1px solid black',
    };
  },
};
</script>

<style vars="{ color, border }">
.text {
  color: var(--color);
  border: var(--border);
}
</style>

在 Vue 3 中的 SFC 中,style 标签支持 vars 绑定,该参数接受对象键值对方式注入 CSS 变量,如上所示 <style vars="{ color }">

这些变量会直接绑定到组件的根元素上,上面的例子中,最后的渲染结果如下:

<div style="--color:red" class="text">hello</div>

和scoped 一起使用

当 vars 和 <style scoped> 一起使用时,所应用的 CSS 变量将以组件的 Scoped id 作为前缀,访问的时候也会自动加上 Scoped id

比如,我们书写如下:

<style scoped vars="{ color }">
h1 {
  color: var(--color);
}
</style>

则编译过后,变成

h1 {
  color: var(--6b53742-color);
}

假如我们这种情况下想访问的是全局的 CSS 变量呢?也就是我们不希望加上 Scoped Id,那么要书写类似如下:

<style scoped vars="{ color }">
h1 {
  color: var(--color);
  font-size: var(--global:fontSize);
}
</style>

这样会编译成如下结果:
h1 {
  color: var(--6b53742-color);
  font-size: var(--fontSize);
}

Less/Sass 中的变量 VS CSS 变量

我理解最重要的一点,就是 CSS 变量可以跟 JavaScript 更好的通信,相当于 CSS 和 JavaScript 的桥梁。在 Vue 中这一点还是体现得挺明显的

另外来看一个切换主题的例子,如果我们用 Sass 变量,如下:

$color-primary: blue;
$color-text: black;
$color-bg: white;
/* invert */
$color-primary-invert: red;
$color-text-invert: white;
$color-bg-invert: black;

.component {
  color: $color-text;
  background-color: $color-bg;

  a {
    color: $color-primary;
  }
}

.component--dark {
  color: $color-text-invert;
  background-color: $color-bg-invert;

  a {
    color: $color-primary-invert;
  }
}

我们有两个主题,一个是普通的主题,一个暗黑模式的(dark)。注意,在暗黑模式中,我们需要新的颜色变量去更新旧的颜色变量。假如这种设置非常多的时候,我们会很苦恼。 看 CSS 变量设置的话

:root, [data-theme="default"] {
  --color-primary: blue;
  /* color contrasts */
  --color-bg: white;
  --color-contrast-lower: hsl(0, 0%, 95%);
  --color-contrast-low: hsl(240, 1%, 83%);
  --color-contrast-medium: hsl(240, 1%, 48%);
  --color-contrast-high: hsl(240, 4%, 20%);
  --color-contrast-higher: black;
}

[data-theme] {
  background-color: var(--color-bg);
  color: var(--color-contrast-high);
}

[data-theme="dark"] {
  --color-primary: red;
  /* color contrasts */
  --color-bg: black;
  --color-contrast-lower: hsl(240, 6%, 15%);
  --color-contrast-low: hsl(252, 4%, 25%);
  --color-contrast-medium: hsl(240, 1%, 57%);
  --color-contrast-high: hsl(0, 0%, 89%);
  --color-contrast-higher: white;
}

这种情况下,我们不需要额外定义一个颜色变量,因为我们只需要设置CSS 变量为正确的值即可

之所以会有以上用法上的不同,我理解是 SASS 变量是编译时,也就是说预处理器在向浏览器输出前已经解析完毕,而浏览器对 CSS 变量解析是运行时的

另外预处理器和 CSS 变量并不冲突,它们结合可以更好的提升我们的开体验

缺点——浏览器兼容性问题

CSS 变量目前的支持度并非特别好,IE 目前全部都是不支持的,但终上所述,依旧看好它的未来