导航


HTML

CSS

JavaScript

浏览器 & 网络

版本管理

框架

构建工具

TypeScript

性能优化

软实力

算法

UI、组件库

Node

业务技能

针对性攻坚

AI


转载自:https://blog.csdn.net/qq_41694291/article/details/105529166

一、Sass/SCSS 使用简介

1. Sass 和 SCSS 的关系

没有用过 Sass 的人很可能会有这样一个疑惑,Sass 和 SCSS 是一个东西吗?

其实本文的标题已经给出了暗示,它们本质上是一个东西。具体来说,SCSS 是 Sass 版本 3 使用的语法。Sass 1.xx 和 Sass 2.xx 采用的是基于缩进的语法,如:

body
  color: red;
  font-size: 12px;

显然,这与 CSS 的写法有很大出入,原生 CSS 是下面这种基于大括号的语法:

body {
  color: red;
  font-size: 12px;
}

这也就意味着早期版本的 Sass 是不能直接兼容原生 CSS 的(即 CSS 代码直接粘贴进 Sass 文件无法编译)。很多前端开发者认为这种语法带来了使用上的不便,为此还催生了兼容原生 CSS 语法的 Less,并且抢占了 Sass 相当多的市场份额。

后来到了版本 3,Sass 团队决定改变之前的缩进风格,改为完全兼容 CSS3 语法的风格,同时把之前的文件后缀.sass改成.scss,以表明该版本已经完全兼容了 CSS3 语法。所以 SCSS 指代的是 Sass 从版本 3 之后开始使用的语法规范,本质上仍然是 Sass 框架。

鉴于 Sass 的版本 3 已经成为其主流版本,因此本文所介绍的内容都将基于 SCSS 语法,除了对一些早期项目维护的需求外,已经没有必要学习早期的 Sass 语法。

2. Sass 简介

从概念上来说,Sass 是一个 CSS 的预处理器,用于辅助进行 CSS 开发。

Sass 为 CSS 开发引入了变量、嵌套、运算、混入、继承、指令控制等一系列接近常规编程语言的语法特性,使得 CSS 开发变得更加简洁和高效。经过编译,Sass 代码可以被转化为原生的 CSS 代码。比如下面是一个符合 scss 语法的文件:

style.scss

.container .main-body {
  div {
    color: black;
  }
  p {
    font-size: 14px;
  }
}

这里 divp 位于 .container .main-body 内表示它们是该选择器的子选择器,它会被编译为以下的 css 文件:

style.css

.container .main-body div {
  color: black;
}

.container .main-body p {
  font-size: 14px;
}

这种嵌套的写法不仅可以减少代码量,还可以清晰地表明它们的包含关系,使得代码的逻辑性更强,非常有利于提升 css 的开发效率。

Sass 是用 Ruby 语言开发的,因此想要编译 .scss 文件,一般来说应该首先安装 Ruby。不过 Nodejs 社区提供了一个非常好用的模块:node-sass,同样可以直接编译 .scss 文件。node-sass 之所以不依赖 Ruby 环境,是因为它将 Nodejs 绑定到了 C 语言版本的 Sass 库:LibSass,而 C 语言环境几乎是各大操作系统的标配。比如:

# 安装node-sass,对于单独的项目也可以局部安装
npm install node-sass -g

# 切换到style.scss所在路径
# 输入以下命令,将style.scss编译为css文件
node-sass style.scss style.css

如果你使用的编辑器是 vs code,官方还推荐了一个非常好用的实时编译插件:Live Sass Compiler。通过对.scss文件进行监视,它可以在每次保存时自动将其编译为 css 文件。

关于 scss 语法,第二部分会有详细的介绍,因此这里暂不深入讲解。

3. 在项目中编译 Sass

前端项目中编译 Sass 文件,本质上还是借助 node-sass 模块,它可以看做是 Sass 代码的解析引擎。我们这里简单介绍一下 webpack 是如何调用 node-sass 模块进行代码编译的。

我们知道,webpack 运行于 Nodejs 环境下,因此它只能直接解析 JavaScript 代码。对于其他类型的文件,webpack 无法读取和识别,于是就需要借助对应的 loader 来加载这些文件。webpack 提供了 sass-loader 来读取和编译 sass 文件,这个 loader 在读取到 .scss 文件的内容后,会调用 node-sass 对这些代码进行编译,生成对应的 css 代码。

生成了 css 代码后,本质上 scss 代码的解析任务就完成了。不过现在你只是得到了普通的 css 代码,为了使 webpack 能识别这些 css 代码,你仍然需要使用解析样式时需要的两个 loader:css-loader 和 style-loader。css-loader 可以把 css 代码解析为 js 模块,这样就可以用 import 直接导入到其他模块中。style-loader 的作用是将 css 代码添加到 style 标签内,内嵌到页面中。因此 webpack.config.js 中应该是下面的写法:

webpack.config.js

module.exports = {
  ...
  module: {
    rules: [
      {
        test: /\\.scss$/,
          use:['style-loader', 'css-loader', 'sass-loader']
       }
    ]
  }
}

webpack 将先调用 sass-loader 将 scss 代码编译为 css 代码(sass-loader 完成该过程依赖的是 node-sass 模块);然后调用 css-loader 将 css 代码封装成合法的 js 模块(这样才可以通过 require 或者 import 导入);最后使用 style-loader 将样式内嵌到页面的 style 标签内。

4. Sass/SCSS、Less 还是 stylus ?

相比于 Less 和 stylus,Sass 的历史最为悠久。由于早期的 Sass 语法没有得到广泛接受,社区诞生了更为轻量,兼容原生 css 语法的 Less,并因此被 Twitter 的著名前端框架 Bootstrap 所采用。一时间,Less 名声大噪,甚至有取代 Sass 的趋势。

不过后来,Sass 在其版本 3 中推出了兼容原生 css 语法的 SCSS,Less 最重要的优势已不复存在。另外,Sass 的功能相对 Less 来说更加强大,也更成熟。因此在 Bootstrap 4 中,依赖的框架已经从 Less 切换为 Sass,这几乎标志着 Sass 与 Less 的竞争已经以 Sass 的胜利告终(另外,即将发布<截止到 2020/4/16>的 Bootstrap 5 已经确定将完全移除对 jQuery 的依赖,转而使用原生的 JavaScript,这一举动也基本预示着 jQuery 的退场)。

相比于 Sass 和 Less,stylus 诞生时间相对较短,暂时没有足够的影响力,不过据说其功能已经可以媲美 Sass,所以关于 Sass 和 stylus 孰优孰劣,本文暂不给出评价。不过如果你希望使用一款更加成熟的预处理器,那么推荐 Sass(SCSS 语法)。

二、Sass(基于 SCSS 语法)详解

1. CSS 功能拓展

🔥 (1). 嵌套规则

这可能是最典型,也最常见的可以体现 sass 是如何提高 css 开发效率的特性之一了。

嵌套规则可以允许你像数学中提取公因式一样,把多个选择器相同的父选择器提取到外部,从而有效地减少选择器的书写量。比如在编写 css 时,我们可能会写出下面的样式:

index.css:

.head {
  margin: 0;
}
.head h1 {
  color: red;
}
.head a {
  color: #555;
  cursor: pointer;
}
.head .content p {
  color: #333;
}
.head .content span {
  font-size: 14px;
}

可以看到,为了定义 .head 选择器下面的 h1、a、p、span 等元素,我们需要把 .head 书写四遍,而且你并不能非常直观地看出这四个元素都是.head的子元素(这需要一定的归纳)。

但是使用 SCSS 语法,你可以写成下面的格式:

index.scss

.head {
  margin: 0;

  h1 {
    color: red;
  }
  a {
    color: #555;
    cursor: pointer;
  }

  .content {
    p {
      color: #333;
    }
    span {
      font-size: 14px;
    }
  }
}

它经过编译之后,就是之前的 css 代码。但显然,用 scss 语法,你可以避免书写大量重复的父选择器。它的另一个优势是,你可以非常直观地看出各个选择的嵌套关系,这可以有效提高修改样式的效率。所以,嵌套语法不可谓不优雅!

🔥 (2). 父选择器 &

在使用嵌套语法的时候可能会遇到这样一个问题,如:

.head a {
  color: #555;
}
.head a:hover {
  color: red;
}

你可能会想到下面的写法,但它与上述 css 是不等价的:

.head a {
  color: #555;
  :hover {
    color: red;
  }
}

/** 它会被编译成 **/
.head a {
  color: #555;
}
.head a :hover {
  color: red;
}

显然,这时:hover不再是选择器a的伪类了,它会导致 a 标签下的所有元素在鼠标悬停时都变成红色。

因此你只能采用下面的写法:

.head {
  a {
    color: #555;
  }
  a:hover {
    color: red;
  }
}

我们认为这看起来并不优雅,因为这并没有达到最大程度的复用,而父选择器则为我们解决了这个问题:

.head a {
  color: #555;
  &:hover {
    color: red;
  }
}

这里的&我们称为父选择器,在这里它指代的就是外部选择器 .head a。sass 引擎在解析这个代码时,会直接用外部选择器替换这个字符,这样你就得到了 .head a:hover 这样的选择器。

值得注意的是,sass 引擎是基于字符串来处理 scss 文件的,在解析过程中,只是简单地把 & 替换成其父选择器对应的字符串。所以你完全可以把 & 看做普通的占位字符与其他字符串拼接,如:

.main {
  margin: 0;
  &-content {
    padding: 0;
  }
}

编译为:

.main {
  margin: 0;
}
.main-content {
  padding: 0;
}

可以看出,引擎是直接把 & 替换成了字符串 .main ,因此得到了选择器 .main-content

另外,存在更深层次的嵌套时,&指代的是所有的外部选择器,如:

.head {
  a {
    &:hover {
    }
  }
}

这里的 & 指代 .head a

需要注意的是,& 必须作为第一个字符,而不能被拼接到其他字符后面,如 .main-& ,这样会报错。

🔥 (3). 属性嵌套

这是嵌套语法的变体。我们上面介绍的嵌套语法指的是选择器的嵌套,除此之外,sass 还支持有相同前缀的属性的嵌套,如:

button {
	font: {
    family: fantasy;
		 size: 16px;
		 weight: bold;
  }
}

等价于:

button {
  font-family: fantasy;
  font-size: 16px;
  font-weight: bold;
}

另外,前缀属性也可以有自己的值,如:

button {
  // 会在上述css代码中额外生成font: 20px/24px; font-size/line-height 简写
  font: 20px/24px {
    family: fantasy;
    size: 16px;
    weight: bold;
  }
}

(4). 占位选择器 %