
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开发者更友好他引入了SCSSSassy SCCSCSS的核心思想是SCSS是CSS的超集这意味着任何合法的CSS代码都是合法的SCSS代码 SCSS保留了大括号和分号也就是说Sass与SCSS的区别就是语法的不同Sass为缩进语法SCSS更贴近CSS原生3、LibSass2009年由于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.cssVSCode插件最推荐的方式在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和each3、error、warn和debug规则CSS语句CSS语句生成CSS可以用在除了function里的任何地方1、样式规则如h1{}2、CSS的规则如media和font-face3、使用include引入Mixin4、at-root规则顶级语句顶级语句只能被用在样式表的顶级根层级或嵌套在顶层的CSS语句内部1、模块加载使用use注Sass中一个模块基本上就是一个.scss文件2、imports新版建议使用use3、Mixin定义使用mixin4、方法定义使用function其他语句1、属性声明只能被用在样式规则和一些CSS规则里2、extend规则只能被用在样式规则里表达式概述表达式是任何可以放在属性或变量声明右侧的内容。每一个表达式产生一个值。任何有效的CSS属性值也是一个Sass表达式但是Sass表达式比简单的CSS值更强大。它们可以作为参数传递给mixin函数用于if规则的控制流并通过算术运算进行操纵。我们将Sass的表达式语法称为SassScript。字面量最简单的表达式只表示静态值1、Numbers可能有或没有单位2、Strings可能有或没有引号3、Colors可以通过十六进制表达式或名字表示如#c6538c或blue4、布尔字面量true或false5、null6、列表字面量7、字典运算表达式1、和!用来检查两个值是否相等2、、-、*、/和%3、、、和4、and、or和notSass认为除了false和null其他值都是true5、、-和/被用来连接字符串6、()被用来显示地控制操作符的优先级其他表达式1、变量如$var2、函数调用如var(--main-bg-color)3、特殊方法如calc(1px100%)4、父选择器表示引用父选择器如.button{ :hover{ background: black; } }5、Sass将!important解析为无引号的字符串如$important: !important; .selector{ color: red $important; }注释Sass中的注释和JS类似分单行注释、多行注释和文档注释1、单行注释也叫silent comments单行注释不会编译到CSS中// 单行注释2、多行注释也叫loud comment如果多行注释写在允许语句出现的位置那么它会被编译成CSS注释多行注释可以包含插值interpolationSass会在编译之前计算好插值表达式的值默认情况下多行注释会被压缩模式下的CSS除去但如果注释以/*!开头则它总会在编译得到的CSS中/* 多行注释 */ /* 插值表达式 * 11#{11}在编译好的CSS中会是112的样子 */ /*! 压缩模式下这个注释也会存在 */ 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 纯CSSimport 带引号或不带引号的字符串 特殊函数 纯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而无需插值。useuse规则从其他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是大小写敏感的。即使使用的是大小写不敏感的文件系统如WindowsSass也会将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:URLsSass使用pkg:URL方案来加载各种包管理器分发的样式表。由于Sass被用于不同编程语言的上下文中这些语言有不同的包管理约定因此pkg:URL几乎没有任何预设的含义。相反鼓励用户实现自定义导入器使用JS API或嵌入式Sass协议使用原生包管理器的逻辑来解析这些URL。即pkg:是Sass预留的接口不是实现。它怎么工作取决于用哪个构建工具或自己写导入器。这使得pkg:URLs以及使用它们的样式表能够在不同的语言生态系统中实现可移植。无论是通过npmSass为其提供了内置的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-importernode当加载一个 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带有引号即使在缩进语法中也是如此。forwardforward规则会加载一个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和includemixin翻译为混入表示将一段样式混入到当前选择器中。对应的include翻译为引入。混入允许你定义可以在整个样式表中重复使用的样式。它们让你能够轻松避免使用像.float-left这样没有语义的类也方便你在库中分发样式集合。混入使用mixin规则定义写法为mixin 名称 {...}或mixin 名称(参数...){...}。混入的名称可以是任何不以--开头的Sass标识符并且它们可以包含除顶层语句外的任何语句。它们可以用来封装样式以便将这些样式放入单个样式规则中。它们可以包含自己的样式规则这些规则可以嵌套在其他规则中也可以包含在样式表的顶层。或者它们也可以仅用于修改变量。混入使用include规则被引入到当前的上下文中其写法为include 名称或include 名称(参数...)其中名称就是被引入的那个混入的名字。参数混入也可以接受参数这让它们每次被调用时的行为都可以被定制。这些参数在mixin规则中定义位于混入名称之后是一组用括号括起来的变量名列表。当使用该混入时必须传入相同数量的参数。这些参数是SassScript表达式。在混入的函数体内这些表达式的值将作为对应的变量被使用。可选参数通常混入声明的每个参数在调用时都必须被传入。但是你可以通过定义默认值来让参数变为可选这样当参数未被传入时就会使用该默认值。默认值的语法与变量声明相同变量名后跟冒号和SassScript表达式。这使得定义灵活且易于使用的混入API成为可能既能简单使用也能复杂定制。事实上默认值可以是任何SassScript表达式它们甚至还可以引用前面的参数。关键字参数当调用混入时除了按照参数列表中的位置顺序传参也可以通过参数名来传递参数。这在混入有多个可选参数时或参数为布尔值且不写名字很难理解其含义时尤其有用。关键字参数使用与变量声明和可选参数相同的语法。注意因为任何参数都可以按名称传递所以在重命名混入的参数时要格外小心这可能会破坏你用户的代码。一个有用的做法是在一段时间内将旧参数名保留为一个可选参数如果有人传入了旧参数名就打印一条警告信息提醒他们迁移到新的参数名上。接受任意参数有时候让一个混入能够接收任意数量的参数是很有用的。如果在mixin声明的最后一个参数名后面加上...那么所有额外的参数都会被当做一个列表传递给这个参数。这个特殊的参数被称为参数列表。参数列表也可以被用来接收任意的关键字参数。meta.keywords()函数接受一个参数列表并返回一个映射Map其中包含了传递给混入的额外关键字参数映射的键是参数名不包含$符号值是这些参数的值。就像参数列表允许混入接收任意的位置参数或关键字参数一样同样的语法也可以用来向混入传递位置参数和关键字参数。如果你在include的最后一个参数位置传入一个列表并加上...那么这个列表的元素会被当作额外的位置参数传入。同理一个Map加上...会被当作额外的关键字参数传入。甚至可以同时传递两者。内容块除了接受参数之外混入还可以接收一整块样式这被称为内容块。混入可以通过在其函数体内包含content规则来声明它可以接收内容块。这个内容块在使用混入时像Sass中其他任何块一样用花括号传入并且它会被插入到混入体内conten规则所在的位置。functionextend总结function和extend直接看官方文档吧边用边看记忆更牢也更好理解。