CSS预处理器是一个能通过预处理器自己独有的语法来生成CSS的程序;即程序员按照预处理器定义的语法编写代码,然后通过编译,将这些代码转换为可以被浏览器识别和渲染的普通CSS;
大多数CSS预处理器会增加一些原生CSS不具备的特性,如变量、混入、嵌套选择器、继承选择器等,这些特性让CSS的结构更加具有可读性且易于维护;
要使用CSS预处理器,须在Web服务器上安装CSS编译工具,或在开发环境中使用CSS预编译器,然后上传编译的CSS文件到Web服务器;
常见的CSS预处理器有Sass/SCSS、LESS、Stylus、PostCSS,处理流程为:
+---------------------+ +------------+ | | compile | | | .scss/.less/.styl +------------->| .css | | | | | +---------------------+ +------------+
当前Sass使用率是最高的,因此直接学习Sass,而Sass中分SCSS和Sass两种语法,SCSS最常用,因此下面的语法都是关于SCSS的语法;
Sass,即Syntactically Awesome Style Sheets;
发展历史
1、诞生
2006年Hampton Catlin希望有一种工具可以让他像编写Ruby代码一样高效、有逻辑地编写CSS,因此他使用Ruby实现了这个想法;
最初的语法是缩进语法(.sass文件),摒弃了CSS中常见的大括号和分号,完全依靠缩进来定义代码结构,对传统CSS开发者来说学习成本较高;
2、SCSS出现
Nathaniel Weizenbaum在Hampton Catlin的基础上,成为了Sass的主要开发者和维护者,2008年时,他意识到缩进语法成为了Sass被更广泛接受的障碍,为了让Sass对CSS开发者更友好,他引入了SCSS(Sassy SCC);
SCSS的核心思想是SCSS是CSS的超集,这意味着任何合法的CSS代码,都是合法的SCSS代码; SCSS保留了大括号和分号;
也就是说,Sass与SCSS的区别就是语法的不同,Sass为缩进语法,SCSS更贴近CSS原生;
3、LibSass
2009年,由于Sass是使用Ruby编写的,编译速度较慢,并且要求用户安装Ruby环境,这对主要使用Node.js技术栈的前端不方便,Hampton Catlin发起了LibSass项目,这是一个用C/C++编写的SASS引擎,其优势为:
极快的编译速度; 可以轻松地被其他语言绑定,迅速成为前端构建工具中的标配; 彻底摆脱了Ruby环境的束缚,成为了一个真正的跨平台、高性能的编译工具;
4、Dart Sass
尽管LibSass非常成功,但它的开发速度跟不上官方Ruby Sass的功能更新,导致了功能差异和版本分裂;
2016年,谷歌的Dart团队使用Dart语言重写了Sass,这就是Dart Sass;
到了2019年,Sass核心团队宣布Dart Sass是官方首选实现;2020年官方正式停止推荐LibSass,并停止了对其新功能的开发,只进行安全维护;
当前,Dart Sass是唯一被官方积极维护和推荐的首先实现,它既可以通过Dart VM运行,也可以编译成JS,无缝集成到任何Node.js或浏览环境中,现在在项目中通过npm install sass安装的就是Dart Sass;
安装Sass
下面是前端常用的安装Sass方式;
npm下载
在安装了Node.js的环境下,通过npm安装Sass,如下:
npm install -g sass
这里安装的是Sass的纯JS实现;
安装完成后,就是可以在命令行上运行Sass来将.scss文件编译成.css文件:
sass input.scss output.css
VSCode插件
最推荐的方式;
在VSCode中安装Live Sass Compiler插件(作者是Gleen Marks);
安装完成后,在VSCode中创建一个.scss文件,点击VSCode状态栏处的【Watch Sass】就会在同级目录下生成对应的.css文件和.css.map文件;
.css.map是json格式的源代码映射文件,连接着编译后的CSS文件和原始的SCSS文件;浏览器通过.map文件,能够反向映射,直接在开发者工具中展示.scss文件,主要是为了方便开发者调试,对性能没有影响,但生产环境中通常会省略;
语法
概述
Sass支持两种语法,各种的文件扩展名为.scss和.sass;
.scss是CSS的超集,几乎所有有效的CSS都是有效的SCSS,使用花括号和分号;
.sass则是使用缩进;
样式表的结构
和CSS类似,大多数Sass样式表主要由包含属性声明的样式规则(style rules)组成。但是Sass样式表除了具备CSS本身的功能外,还拥有许多其他特性,这些特性可以与CSS现有的功能并存,共同提升样式编写的效率和可维护性。
语句
概述
一个Sass样式表由一系列语句(statements)组成,这些语句按顺序被评估,以生成最终的CSS。一些语句可能有块,使用{}来定义,里面包含其他语句。例如,一个样式规则是带有块的语句,块中包含其他语句,如属性声明。
在SCSS中,语句被分号隔开(如果语句使用块的话分号可以不写)。在缩进语法中,语句使用新行隔开。
通用语句
通用语句可以在Sass样式表的任何地方使用:
1、变量声明
2、流程控制的@规则,如@if和@each
3、@error、@warn和@debug规则
CSS语句
CSS语句生成CSS,可以用在除了@function里的任何地方:
1、样式规则,如h1{}
2、CSS的@规则,如@media和@font-face
3、使用@include引入Mixin
4、@at-root规则
顶级语句
顶级语句只能被用在样式表的顶级(根层级)或嵌套在顶层的CSS语句内部:
1、模块加载,使用@use
注:Sass中,一个模块基本上就是一个.scss文件;
2、@imports(新版建议使用@use)
3、Mixin定义,使用@mixin
4、方法定义,使用@function
其他语句
1、属性声明,只能被用在样式规则和一些CSS@规则里
2、@extend规则只能被用在样式规则里
表达式
概述
表达式是任何可以放在属性或变量声明右侧的内容。每一个表达式产生一个值。任何有效的CSS属性值也是一个Sass表达式,但是Sass表达式比简单的CSS值更强大。它们可以作为参数传递给mixin函数,用于@if规则的控制流,并通过算术运算进行操纵。我们将Sass的表达式语法称为SassScript。
字面量
最简单的表达式,只表示静态值:
1、Numbers,可能有或没有单位
2、Strings,可能有或没有引号
3、Colors,可以通过十六进制表达式或名字表示,如#c6538c或blue
4、布尔字面量,true或false
5、null
6、列表字面量
7、字典
运算表达式
1、==和=!用来检查两个值是否相等
2、+、-、*、/和%
3、<、<=、>和>=
4、and、or和not,Sass认为除了false和null,其他值都是true
5、+、-和/,被用来连接字符串
6、()被用来显示地控制操作符的优先级
其他表达式
1、变量,如$var;
2、函数调用,如var(--main-bg-color);
3、特殊方法,如calc(1px+100%);
4、父选择器,&(表示引用父选择器),如:
.button{ &:hover{ background: black; } }5、Sass将!important解析为无引号的字符串,如:
$important: !important; .selector{ color: red $important; }注释
Sass中的注释和JS类似,分单行注释、多行注释和文档注释;
1、单行注释
也叫silent comments;单行注释不会编译到CSS中;
// 单行注释
2、多行注释
也叫loud comment;
如果多行注释写在允许语句出现的位置,那么它会被编译成CSS注释;
多行注释可以包含插值(interpolation),Sass会在编译之前计算好插值表达式的值;
默认情况下,多行注释会被压缩模式下的CSS除去,但如果注释以/*!开头,则它总会在编译得到的CSS中;
/* 多行注释 */ /* 插值表达式: * 1+1=#{1+1}在编译好的CSS中会是1+1=2的样子 */ /*! 压缩模式下这个注释也会存在 */ p/* 这个多行注释不会被编译CSS显示,它不在允许语句出现的位置 */.sans{ font: Helvetica; }3、文档注释
文档注释是silent comments,写在要文档化的元素的上方;
SassDoc通过解析Sass的文档注释,生成漂亮的文档;其常用注解有:
@type:变量类型(Color、Number、String、List、Map、Boolean); @param:函数/混合器参数; @return:函数返回值; @author:作者信息; @throw:可能抛出的错误;
SassDoc的使用方式:
# 安装SassDoc npm install -g sassdoc # 生成文档 sassdoc path/to/sass-files --dest path/to/docs
文档注释实例如下:
/// 这是文档注释 /// /// @type Color $primary-color: #349822 !default;
特殊函数
CSS定义了许多函数,其中大多数都能很好地配合Sass的正常函数语法使用。它们会被解析为函数调用,转换为普通的CSS函数,并原样编译到CSS中,因为Sass的函数调用语法与CSS函数语法相同。
但是有一些例外的情况,它们(CSS函数)具有特殊的语法,不能简单地作为SassScript表达式解析。如:
calc函数中
$padding: 100px; .element{ width: calc(100% - $padding + 5px); // Sass可能错误的计算:100% -100px + 5px // 但calc()需要在浏览器中计算,不能预处理 } // 使用插值能避免Sass计算 .element{ width: calc(100% -#{$padding} + 5px); }所有特殊函数调用都返回无引号的字符串。
样式规则
Sass的样式规则和CSS类似,通过选择器选择要设计样式的元素,然后声明影响元素外观的属性。
概述
嵌套
但是Sass更简单,它不再重复声明相同的选择器,而是可以在样式规则内再写样式规则,Sass会自动将外部的规则选择器与内部的规则选择器结合,这就是嵌套(Nesting);如
nav{ ul{ margin: 0; padding: 0; } li{ display: inline-block; } } // 等同于下面的CSS nav ul{ margin: 0; padding: 0; } nav li{ display: inline-block; }注意:嵌套不用太深;多种写法可以查看官方文档;
插值
可以使用插值,将来自表达式(如变量和函数调用)的值注入到选择器中。在Sass中,变量如($name)通常用于设置属性值,比如:
color: $name;
但如果想把变量的值用在选择器或属性名上,Sass无法直接识别,这个时候就需要插值来告诉Sass,这不是一个普通的CSS类名,而是一个需要计算的变量。也就是说,变量用于值,插值用于名称。插值的语法是#{},如:
$theme-dark: dark; .theme-#{$theme-dark} { color: red; }属性声明
和CSS一样,在Sass中,属性声明定义了与选择器匹配的元素将如何被样式化。但Sass添加了额外的功能,使它们更易于编写和自动化。首先也是最重要的,声明值可以是任何SassScript表达式,该表达式将被计算并包含在结果中。
插值
属性名称可以包含插值,这使得可以根据需求动态生成属性,甚至可以插入整个属性名。
嵌套
许多CSS属性以相同的前缀开始,作为一种名称空间,如font-family、font-size这些,都以font-开始。Sass允许属性声明嵌套,外部的属性名可以被加到内部的属性名上,以连字符隔开。
有些CSS属性有使用命名空间作为属性名的简写版本。对于这些属性,可以同时编写简写值和更明确的嵌套版本。如一个简写属性(font、margin、border),多个详细属性(font-size、margin-top)。
如:
.enlarge { font-size: 14px; transition: { property: font-size; duration: 4s; } margin: auto { bottom: 10px; top: 2px; } &:hover{ font-size: 36px; } } // 等同于下面的CSS .enlarge { fontsize: 14px; transition-property: font-size; transition-duration: 4s; margin: auto; margin-bottom: 10px; margin-top: 2px; } .enlarge:hover { font-size: 36px; }隐藏声明
有时候只想让属性声明在某些时候出现。如果声明的值是null或空的无引号字符串,Sass根本不会将该声明编译到CSS中。如:
$rounded-corners: false; .button{ border: 1px solid black; border-radius: if(sass($rounded-corners): 5px); } // 等同于下面的CSS .button{ border: 1px solid black; }自定义属性
概述
CSS自定义属性,也叫CSS变量,具有不同寻常的声明语法:它们允许在声明值中使用几乎任何文本。更重要的是,这些值可以被JavaScript访问,因此任何值都可能与用户相关。这包括通常会被解析为SassScript的值,即尽管CSS变量的值看起来像SassScript语法,Sass也不会解析它们。
若需要Sass计算,则在声明值中使用插值。如:
$spacing: 1rem; :root{ --spacing: #{$spacing}; --computed: #{$spacing*2}; } .element{ margin: var(--computed); } // 等同于下面的CSS :root{ --spacing: 1rem; --computed: 2rem; } .element{ margin: var(--computed); }@function结果
纯CSS的@function规则的结果属性的工作方式类似于自定义属性:它可以被包含在属性值的任何位置,可以从JavaScript访问,因此它可以接受任何可能的值。Sass解析它的方式与解析自定义属性值相同,这意味着必须使用插值来在其中包含SassScript值。
父选择器&
父选择器&是Sass创造的特殊选择器,用于在嵌套选择器中引用外部选择器。它使得能以更复杂的方式重用外层选择器,比如添加伪类或在父选择器前添加选择器;
在Sass的嵌套规则中,&代表当前嵌套规则的外层选择器。如:
.alert{ &:hover{ font-weight: bold; } :not(&){ opacity: 0.8; } } // 等同于下面的CSS .alert:hover { font-weight: bold; } :not(.alert){ opacity: 0.8; }注意:因为父选择器可能被替换为像h1这样的类型选择器,所以它只允许出现在复合选择器中类型选择器可以出现的位置(即开头)。例如,span&是不允许的;
添加后缀
可以使用父选择器向外层选择器追加额外的后缀。这在使用像BEM这样具有高度结构化类名的方法论时特别有用。只要外层选择器以字母数字名称结尾(如类选择器、ID选择器、元素选择器),就可以使用父选择器来追加额外的文本。
无嵌套
就像在普通CSS中一样,也可以在嵌套规则外使用父选择器。在这种情况下,它会被作为普通CSS输出,由浏览器处理,浏览器会将其解释为作用域根(scoping root)。
& { background-color: blue; } /* 浏览器将其解析为::scope { background-color: blue; } */ /* 最终效果通常等同于 html { background-color: blue; } */作用域根:
在style标签或CSS文件顶层,作用域根是:scope,通常指向html根目录或当前影子DOM的根。
占位符选择器%
placeholder selectors;和类选择器很像,但是占位符选择器以%开头,并且不输出到CSS中;
事实上,选择器包含占位符选择器的,都不会被编译到CSS中,如:
.alert:hover,%strong-alert { font-weight: bold; } %strong-alert:hover { color: red; } // 等同于下面的CSS .alert:hover { font-weight: bold; }占位符选择器的正确打开方式是使用@extend;
变量
Sass中的变量就是:将一个值赋给一个以$开头的名字,然后就可以用这个名字来代替值本身。尽管它们很简单,但却是Sass提供的最有用的工具之一。变量使得减少重复、进行复杂数学运算、配置库以及更多其他功能成为可能。
变量声明看起来很像属性声明。不像属性只能声明在样式规则或@规则中,变量可以在任何地方声明。
只有在满足以下标准时才适合创建变量:
该值至少重复出现了两次; 该值至少可能会被更新一次,但有的团队即使不更新也会定义,本规则具有主观性; 不因为两个值偶然相等就把它们绑定在同一个变量上,只有当在概念上代表同一个东西时,才公用一个变量;
此外,CSS有自己的变量,这和Sass的变量完全不同。
CSS变量的定义:
:root { --primary-color: red; } .button { color: var(--primary-color); }Sass的变量和CSS3变量的不同点有:
Sass变量都会被Sass编译掉。CSS变量会被保留在CSS输出中; CSS变量可以针对不同的元素有不同的值,但Sass变量一次只能有一个值; Sass变量是命令式的,这意味着如果使用了一个变量然后改变了它的值,之前的使用保持不变。CSS变量是声明式的,这意味着如果改变它的值,会同时影响之前和之后的使用;
对于Sass变量,就像所有Sass标识符一样,将连字符和下划线视为相同。这意味着$font_size和$font-size指向同一个变量。这是Sass早期历史遗留问题,当时Sass只允许标识符中使用下划线,后来Sass添加了对连字符的支持以匹配CSS语法,为了便于迁移,两者被设为等价。
默认值
通常,当给变量时,如果该变量已经有值,旧值会被覆盖。但是,如果正在编写一个Sass库,可能会希望允许用户在使用这些变量生成CSS之前,对库中的变量进行配置。为了实现这一点,Sass提供了!default标志。
这个标志只在变量未定义或其值为null时才为该变量赋值。否则,将使用已有的值。也就是说若用户未定义相关变量,则使用默认值,否则该变量未用户定义的值。
另外,只有在样式表顶层(顶级作用域)使用!default标志编写的变量才能被配置。
内置模块变量
由内置模块定义的变量不能被修改。
内置模块指的是Sass提供的一系列内置模块,每个模块包含预定义的函数和变量。
作用域
变量的作用域指的是变量在代码中哪些地方可以被访问或修改;
在样式表顶层声明的变量是全局的。这意味着它们声明之后,可以在模块中的任何地方被访问。但并非所有变量都是如此。在代码块中声明的变量(SCSS中的花括号或Sass中的缩进代码)通常是局部的,只能在声明它们的块内被访问。
遮蔽
局部变量甚至可以声明为与全局变量同名。如果发生这种情况,实际上存在两个同名但不同的变量:一个是局部的,一个是全局的。这有助于确保开发人员在编写局部变量时,不会意外改变他们甚至可能不知道存在的全局变量的值。
Sass中变量声明的作用域默认是局部的,即:
变量在它被定义的层级(或任何嵌套在它内部的层级)是可用的; 在外部作用域定义的变量,在内部作用域可以访问; 在内部作用域定义的变量,在外部作用域无法访问;
如果已经存在同名的全局变量,则局部变量覆盖全局变量;
如果需要从局部作用域设置全局变量的值(例如在mixin中),可以使用!global标志。标记为!global的变量声明将始终赋值给全局作用域。
注意1:!global是一种有意破坏局部作用域保护机制的手段。大多数情况下应该避免使用,只在确实需要维护全局状态时才考虑。如果使用,务必确保清楚的知道在做什么并做好文档记录。
注意2:!global标志只能用于设置已经在文件顶层声明过的变量。它不能用于声明一个新变量。
控制流作用域
控制流指的是if、for、each、while等控制代码执行流向的指令。
在控制流规则中声明的变量具有特殊的作用域规则:它们不会遮蔽与控制流规则同级别的变量。相反,它们只是对这些变量进行赋值。这使得有条件地给变量赋值,或在循环中逐步构建一个值变得更加容易。
注意:控制流作用域中的变量可以给外部作用域中已存在的变量赋值,但在控制流作用域中声明的新变量无法在外部作用域中访问。确保在复制之前变量已经被声明,即使你需要将它声明为null。
插值
插值几乎可以在Sass样式表的任何地方使用,用于将SassScript表达式的结果嵌入到CSS代码块中。只需要在以下任何位置用#{}包裹表达式即可:
样式规则中的选择器; 声明中的属性名; 自定义属性值; CSS@规则; @extend; 纯CSS@import; 带引号或不带引号的字符串; 特殊函数; 纯CSS函数名; loud comments;
在SassScript中
插值可以在SassScript中使用,用于将SassScript表达式注入到无引号字符串中。这在动态生成名称(例如动画名称)或使用斜杠分隔的值时特别有用。注意,SassScript中的插值总是返回无引号的字符串。
实际上,插值对于将值注入到字符串中很有用,但在SassScript表达式中,除了这个用途外,它很少是必需的。在属性值中仅仅使用一个变量时,绝对用不到插值。与其写color:#{$accent},只要写color:$accent就行了。
注意:对数字使用插值是个坏主意。插值返回无引号字符串,无法用于任何进一步的数学运算,并且它会绕过Sass内置的、用于确保单位正确使用的保护机制。不要写#{$width}px,而是用$width*1px,或者说一开始就把$width变量定义为以px为单位。
带引号字符串
在大多数情况下,插值注入的文本与表达式作用属性值时使用的文本完全相同。但有一个例外:带引号字符串周围的引号会被移除(即使这些带引号字符串在列表中也是如此)。这使得把选择器等SassScript不支持的语法(即SassScript本身不允许直接写选择器作为值)写在字符串里成为可能,并将它们插值到样式规则中。
注意:虽然使用插值的特性将带引号字符串转换为无引号字符串很有诱惑力,但使用string.unquote()函数要清晰得多。不要写#{string},而是应该写string.unquote($string)。
@规则
Sass的大部分额外功能,都以在CSS基础之上新增的@规则形式呈现的:
@use:从其他Sass样式表中加载混入、函数和变量,并将多个样式表的CSS合并在一起; @forward:加载一个Sass样式表,并让自己编写的样式表在被@use规则加载时,能够提供被加载样式表中的混入、函数和变量; @import:扩展了CSS的@规则,使其能从其他样式表中加载样式、混入、函数和变量; @mixin和@include:使得复用大段的样式变得容易; @function:定义可以在SassScript表达式中使用的自定义函数; @extend:允许选择器之间相互继承样式; @at-root:将其内部的样式直接放到CSS文档的根层级; @error:导致编译失败,并输出一条错误信息; @warn:在不终止编译的情况下输出一条警告信息; @debug:输出一条用于调试的信息; 控制流规程:@if、@each、@for和@whie,用于控制样式是否被输出,或者被输出多少次;
此外,Sass对普通CSS的@规则也有一些特殊处理:它们可以包含插值,并且可以被嵌套在样式规则中。其中一些规则(如@media)甚至允许直接在规则本身中直接使用SassScript,而无需插值。
@use
@use规则从其他Sass样式表中加载混入、函数和变量,并将多个样式表中的CSS合并在一起。被@use加载的样式表称为模块。Sass还提供了内置模块,其中包含许多有用的函数。
最简单的@use规则是@use "<url>",它会加载指定URL处的模块。以这种方式加载的任何样式,在编译后的CSS输出中都只会被包含一次,无论这些样式被加载了多少次。
注意:样式表中的@use规则必须出现在除@forward之外的任何其他规则之前,包括样式规则。但是,可以在@use规则之前声明变量,以便在配置模块时使用。
加载成员
可以通过<namespace>.<variable>、<namespace>.<function>()或@include <namespace>.<mixin>()的方式来访问其他模块的变量、函数和混入。默认情况下,命名空间就是模块URL的最后一部分。
使用@use加载的成员(变量、函数和混入)仅在加载它们的样式表中可见。其他样式表如果需要访问这些成员,也必须编写自己的@use规则。这有助于轻松追踪每个成员的确切来源。如果想要一次性从多个文件中加载成员,可以使用@forward规则将它们全部从一个共享文件中转发出去。
实际上,因为@use会给成员名称添加命名空间前缀,所以在编写样式表时,可以放心地选择非常简单的名字,如$radius。这与旧的@import规则不同,后者会鼓励用户使用像$mat-corner-radius这样的长名称来避免与其他库冲突。@use的这一特性有助于保持样式表的清晰易读。
选择命名空间
默认情况下,模块的命名空间就是其URL的最后一部分(不含文件扩展名)。但有时可能想要选择不同的命名空间——比如为一个频繁引用的模块使用更短的名字,或正在加载多个具有相同文件名的模块。这可以通过@use "<url>" as <namespace>来实现。
也可以通过@use "<url>" as *来加载一个不带命名空间的模块。不过,建议只对自己编写的样式表这样做;否则,它们可能会引入导致命名冲突的新成员。
私有成员
作为样式表的作者,可能不希望定义的所有成员都能在样式表外部被访问。Sass允许通过-或_开头来轻松定义私有成员。这些成员在定义它们的样式表内部正常工作,但它们不会成为模块公共API的一部分。这意味着加载此模块的样式表无法看到它们。
实际上,如果想让一个成员在整个包的范围内私有,而不是仅仅在单个模块内私有,只需不要从包的任何一个入口文件(用户实际@use的文件,通常是index.scss)中转发(@forward)出它的模块。甚至可以在转发模块的其他部分时隐藏该成员,也就是说这个时候不需要将成员设为私有。
配置
样式表可以使用!default标志来定义变量,使其成为可配置的。要加载带配置的模块,可以写@use <url> with (<variable>:<value>,<variable>:<value>)。配置的值将覆盖变量的默认值。
一个模块即使被多次加载,也会保持相同的配置(或未配置状态)。因此,@use ... with每个模块只能使用一次,且必须是该模块第一次被加载的时候。这也使得可以使用配置来创建在整次编译中全局生效的主题。
注意:如果想配置一个模块并给它一个自定义命名空间,as子句必须在with子句之前,如@use ... as ... with ...;
使用mixins配置
使用@use ... with配置模块非常方便,特别是在使用哪些原本为@import规则编写的库时。但这种方式的灵活性有限,对于更高级的用例,不推荐使用。如果想要:一次性配置许多变量、传递Map作为配置,或在模块加载后更新配置,那么考虑改用一个mixin来设置变量,另一个mixin来注入样式,而不是使用@use ... with的形式。
重赋值变量
在加载一个模块后,可以在当前文件中为该模块的变量(非私有变量)重新赋值。当然,修改只会在当前文件生效。
即使使用as *这种吴明明空间的方式导入一个模块,这同样适用。对模块中定义的变量名进行赋值,将会覆盖该模块中该变量的值。
再次提醒,内置模块的变量无法重新赋值,如math.$pi。
查找模块
如果要为加载的每个样式表都写出完整的绝对URL,很麻烦。因此,Sass的模块查找算法让这件事变得简单一些。首先,不需要显式地写出要加载的文件扩展名;@use "variables"会自动加载variables.scss、variables.sass或variables.css。
注意,为了确保样式表能在所有操作系统上工作,Sass通过URL而不是文件路径加载文件。这意味着即使在Windows上,也必须使用正斜杠(/)而不是反斜杠(\)。同时,URL是大小写敏感的。即使使用的是大小写不敏感的文件系统(如Windows),Sass也会将Styles.scss和styles.scss视为不同的模块。请确保URL与实际磁盘文件的大小写一致,否则样式表可能会被加载两次且肯定无法在其他操作系统上工作。
加载路径
所有Sass实现都允许用户提供加载路径:即文件系统上的路径,Sass在定位模块时会去这些路径中查找。如,将node_modules/susy/sass作为加载路径,就可以用@use "susy"来加载node_modules/susy/sass/susy.scss(虽然pkg:URLs是处理此问题的更好方式)。
加载路径:load-path,加载路径的配置方式有命令行、API和构建工具。即告诉Sass额外的搜索目录。
不过,模块始终优先对相对于当前文件的进行加载。只有当不存在与模URL匹配的相对文件时,才会使用加载路径。这确保了添加新库时不会意外破坏相对导入。
不像其他一些语言,Sass不要求在相对导入中使用./。相对导入始终可用。
局部文件
按照惯例,那些只打算作为模块被加载、而不是独立编译的Sass文件,以下划线_开头(如_code.scss)。这些文件被称为局部文件(partials),它们告诉Sass工具不用尝试独立编译这些文件,在导入局部文件时,可以省略开头的下划线。
索引文件
如果在文件夹中写了_index.css,那么当加载该文件夹的URL时,这个Index文件会被自动加载。
pkg:URLs
Sass使用pkg:URL方案来加载各种包管理器分发的样式表。由于Sass被用于不同编程语言的上下文中,这些语言有不同的包管理约定,因此,pkg:URL几乎没有任何预设的含义。相反,鼓励用户实现自定义导入器(使用JS API或嵌入式Sass协议),使用原生包管理器的逻辑来解析这些URL。即pkg:是Sass预留的接口,不是实现。它怎么工作,取决于用哪个构建工具或自己写导入器。
这使得pkg:URLs以及使用它们的样式表能够在不同的语言生态系统中实现可移植。无论是通过npm(Sass为其提供了内置的pkg:导入器)安装Sass库,还是通过最冷门的包管理器安装,只要写@use 'pkg:library',它都能正确地工作。
注意,pkg:URLs不仅限于@use规则。可以在任何需要加载Sass文件的地方使用它们,包括@forward、meta.load-css(),甚至是旧的@import规则。
pkg:导入器的规则
有一些常见规则,Sass期望所有pkg:导入器都能遵守。这些规则有助于确保pkg:URLs在所有包管理器中的处理方式一致,从而使样式表具有最大程度的可移植性。
除了自定义导入器的标准规则外,pkg:导入器必须只处理满足以下条件的非规范URL:
具有pkg协议 其路径以包名开头 可选地后跟路径,路径段用正斜杠分隔
包名可能包含正斜杠,具体取决于特定的包管理器是否支持。例如,npm允许像@namepace/name这样的包名。需要注意的是,包含非字母数字字符的包名在不同包管理器之间的可移植性可能较差。
pkg:导入器必须拒绝以下模式的URL:
路径以/开头的URL; 带有非空/非空的用户名、密码、主机、端口、查询参数或片段的URL;
如果 pkg: 导入器遇到一个 URL,它违反了自身包管理器的约定但没有违反上述规则,它应该只是拒绝加载该 URL,而不是抛出错误。这允许用户在必要时同时使用多个 pkg: 导入器。
Node.js包导入器
由于 Sass 最常与 Node.js 生态系统一起使用,它附带了一个 pkg:导入器,该导入器使用与 Node.js 相同的算法来加载 Sass 样式表(在node_modules中查找包)。这个导入器默认不可用,但很容易启用:
如果使用JavaScript API,只需在importers选项中添加 new NodePackageImporter() 如果使用Dart API,在importers选项中添加NodePackageImporter() 如果使用命令行,传入--pkg-importer=node
当加载一个 pkg: URL 时,Node.js pkg: 导入器会查看包的 package.json文件来决定加载哪个 Sass 文件。它会按顺序检查:
"exports" 字段,附带条件 "sass"、"style" 和 "default"。这是推荐包暴露 Sass 入口点的方式。 "sass" 字段或 "style" 字段,应该是指向 Sass 文件的路径。这仅在 pkg: URL 没有子路径时有效——pkg:library 会加载 "sass" 字段中列出的文件,但 pkg:library/button 会从包的根目录加载 button.scss。 包根目录的 index 文件。这也仅在 pkg: URL 没有子路径时有效。"
注:
在package.json文件中,exports字段提供了一个比main更现代的替代方案,它允许定义多个入口点,支持不同环境之间的条件化入口解析,并且阻止除了exports中定义的入口之外的其他任何入口点被访问。这种封装机制使得模块作者能够清晰地定义起包的公共接口。
与@import的区别
@use 规则旨在取代旧的 @import规则,但它被刻意设计为以不同的方式工作。以下是两者之间的一些主要区别:
use只让变量、函数和混入在当前文件的作用域内可用。它永远不会将它们添加到全局作用域。这使得可以轻松找出Sass文件中每个名字的来源,并且意味着可以使用更短的名字而没有任何冲突风险。 @use每个文件只加载一次。这确保你不会意外地多次重复输出依赖项的 CSS。 @use必须出现在文件开头,不能嵌套在样式规则中。嵌套的导入可以迁移为混入调用或 meta.load-css()。 每个@use规则只能有一个URL。 @use要求URL带有引号,即使在缩进语法中也是如此。
@forward
@forward规则会加载一个Sass样式表,并让你的样式表被@use规则加载时,将这个被加载样式表中的混入、函数和变量提供给使用者。它让你能够将Sass库分散组织在多个文件中,同时允许库的用户只需加载一个单一的入口文件。
该规则的写法是@forward "<url>"。它会像@use一样加载指定URL处的模块,但它会将所加载模块的公共成员暴露给你模块的用户,就好像这些成员是直接在你的模块中定义的一样。不过这些成员在你的模块中并不可用,如果想使用的话,需要再写一个@use规则,这样该模块也只会加载一次。
如果在同一个文件中同时为同一个模块写了@forward和@use,那么最好把@forward写在前面。这样一来,如果你的用户(此时你是工具模块编写者)想要配置这个被转发的模块,该配置会在你的@use加载该模块(不带任何配置)之前,先应用于@forward。
实际上,当涉及到模块的CSS时,@forward规则的行为与@use完全一样。被转发的模块中的样式会被包含在编译后的CSS输出中,而且使用@forward的模块可以@extend它,即使该模块没有@use。
添加一个前缀
因为模块成员通常通过命名空间来使用,所以简短、简单的名字通常是最可读的选择。但这些名字在它们被定义的模块之外可能没有意义,因此@forward提供了为所有转发的成员添加额外前缀的选项。
这个语法的写法是@forward "<url>" as <prefix>-*,它会将给定的前缀添加到模块转发的每个mixin、function和变量名的开头。如,模块定义一个名为reset的成员,并且以list-*的形式@forward,那么下游的样式表将使用list-reset来引用它。
控制可见性
有时不想转发模块中的每个成员。可能想让一些成员保持私有,只让包内部使用;或者可能想让用户通过其他方式加载某些成员。可以通过@forward "<url>" hide <members>或@forward "<url>" show <members>来精确控制哪些成员被转发。
hide形式表示:列出的成员不转发,其他成员都转发。show的形式表示:只转发列出的成员。两种形式中,都要列出mixin、function或变量的名称(变量名要带上$)。
配置模块
@forward规则也可以用配置来加载模块。基本上和@use的配置方式相同,但有一个额外的特性:@forward规则的配置中可以使用!default标志。这使得一个模块可以修改上游样式表的默认值,同时仍然允许下游样式表覆盖这些值。
@mixin和@include
@mixin翻译为混入,表示将一段样式混入到当前选择器中。对应的@include翻译为引入。
混入允许你定义可以在整个样式表中重复使用的样式。它们让你能够轻松避免使用像.float-left这样没有语义的类,也方便你在库中分发样式集合。
混入使用@mixin规则定义,写法为@mixin <名称> {...}或@mixin 名称(参数...){...}。混入的名称可以是任何不以--开头的Sass标识符,并且它们可以包含除顶层语句外的任何语句。它们可以用来封装样式,以便将这些样式放入单个样式规则中。它们可以包含自己的样式规则,这些规则可以嵌套在其他规则中,也可以包含在样式表的顶层。或者它们也可以仅用于修改变量。
混入使用@include规则被引入到当前的上下文中,其写法为@include <名称>或@include <名称>(<参数...>),其中名称就是被引入的那个混入的名字。
参数
混入也可以接受参数,这让它们每次被调用时的行为都可以被定制。这些参数在@mixin规则中定义,位于混入名称之后,是一组用括号括起来的变量名列表。当使用该混入时,必须传入相同数量的参数。这些参数是SassScript表达式。在混入的函数体内,这些表达式的值将作为对应的变量被使用。
可选参数
通常,混入声明的每个参数在调用时都必须被传入。但是,你可以通过定义默认值来让参数变为可选,这样当参数未被传入时,就会使用该默认值。默认值的语法与变量声明相同:变量名,后跟冒号和SassScript表达式。这使得定义灵活且易于使用的混入API成为可能,既能简单使用,也能复杂定制。
事实上,默认值可以是任何SassScript表达式,它们甚至还可以引用前面的参数。
关键字参数
当调用混入时,除了按照参数列表中的位置顺序传参,也可以通过参数名来传递参数。这在混入有多个可选参数时,或参数为布尔值且不写名字很难理解其含义时,尤其有用。关键字参数使用与变量声明和可选参数相同的语法。
注意,因为任何参数都可以按名称传递,所以在重命名混入的参数时要格外小心,这可能会破坏你用户的代码。一个有用的做法是:在一段时间内,将旧参数名保留为一个可选参数,如果有人传入了旧参数名,就打印一条警告信息,提醒他们迁移到新的参数名上。
接受任意参数
有时候,让一个混入能够接收任意数量的参数是很有用的。如果在@mixin声明的最后一个参数名后面加上...,那么所有额外的参数都会被当做一个列表传递给这个参数。这个特殊的参数被称为参数列表。
参数列表也可以被用来接收任意的关键字参数。meta.keywords()函数接受一个参数列表,并返回一个映射(Map),其中包含了传递给混入的额外关键字参数,映射的键是参数名(不包含$符号),值是这些参数的值。
就像参数列表允许混入接收任意的位置参数或关键字参数一样,同样的语法也可以用来向混入传递位置参数和关键字参数。如果你在@include的最后一个参数位置传入一个列表并加上...,那么这个列表的元素会被当作额外的位置参数传入。同理,一个Map加上...会被当作额外的关键字参数传入。甚至可以同时传递两者。
内容块
除了接受参数之外,混入还可以接收一整块样式,这被称为内容块。混入可以通过在其函数体内包含@content规则来声明它可以接收内容块。这个内容块在使用混入时,像Sass中其他任何块一样用花括号传入,并且它会被插入到混入体内@conten规则所在的位置。
@function
@extend
总结
@function和@extend直接看官方文档吧,边用边看记忆更牢也更好理解。