导航
转载自:https://blog.csdn.net/qq_41694291/article/details/105529166
没有用过 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 语法。
从概念上来说,Sass 是一个 CSS 的预处理器,用于辅助进行 CSS 开发。
Sass 为 CSS 开发引入了变量、嵌套、运算、混入、继承、指令控制等一系列接近常规编程语言的语法特性,使得 CSS 开发变得更加简洁和高效。经过编译,Sass 代码可以被转化为原生的 CSS 代码。比如下面是一个符合 scss 语法的文件:
style.scss
.container .main-body {
div {
color: black;
}
p {
font-size: 14px;
}
}
这里 div 和 p 位于 .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 语法,第二部分会有详细的介绍,因此这里暂不深入讲解。
前端项目中编译 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 标签内。
相比于 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 是如何提高 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 语法,你可以避免书写大量重复的父选择器。它的另一个优势是,你可以非常直观地看出各个选择的嵌套关系,这可以有效提高修改样式的效率。所以,嵌套语法不可谓不优雅!
&在使用嵌套语法的时候可能会遇到这样一个问题,如:
.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-& ,这样会报错。
这是嵌套语法的变体。我们上面介绍的嵌套语法指的是选择器的嵌套,除此之外,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;
}
}
%