【Sass/SCSS 完整自學中文版教程01】SCSS 官方英文文件翻譯整理
Sass 基本介紹
目錄如果對本文有任何問題,建議,或者在前端技術體系方面有任何問題,可以新增我的微信: drylint , 我會盡可能為你解答,也會拉你進入前端技術進階交流群,大家一起進步~
Sass 是 CSS 的超集,支援所有 css 語法,並在其基礎上擴充套件。
Sass 支援像 css 一樣的大括號語法,副檔名為 .scss
,以及另一種使用縮排的語法,副檔名為 .sass
。
教程主要採取完全相容 css 的 SCSS 語法。
註釋(Comments)
支援兩種註釋,分別是:
- 單行註釋
// 註釋文字
- 多行註釋
/* 註釋文字 */
單行註釋(Single-line comments)
編譯的時候會直接被忽略,不會編譯到 CSS 中,所以也叫做“隱式註釋”(silent comments)。
// 註釋內容
多行註釋(Multi-line comments)
編譯時會將註釋編譯到 css 中,所以也叫做“顯式註釋”(loud comment)
// 這一行註釋不會出現在編譯的 css 中 /* 這一行會出現在編譯的 css 中,除非是在壓縮模式下則不會 */ /* 註釋中還可以包含插值: * 1 + 1 = #{1 + 1} */ /*! 這行註釋即使在壓縮模式下也會編譯到 css 中 */ p /* 多行註釋可以寫在任何 * 允許空白出現的地方 */ .sans { font-size: 16px; }
編譯後的 css:
/* 這一行會出現在編譯的 css 中,除非是在壓縮模式下則不會 */
/* 註釋中還可以包含插值:
* 1 + 1 = 2 */
/*! 這行註釋即使在壓縮模式下也會編譯到 css 中 */
p .sans {
font-size: 16px;
}
SassDoc
文件註釋,類似於 jsdoc 。使用三斜線 ///
宣告。
/// Computes an exponent.
///
/// @param {number} $base
/// The number to multiply by itself.
/// @param {integer (unitless)} $exponent
/// The number of `$base`s to multiply together.
/// @return {number} `$base` to the power of `$exponent`.
@function pow($base, $exponent) {
$result: 1;
@for $_ from 1 through $exponent {
$result: $result * $base;
}
@return $result;
}
特殊的函式(Special Functions)
- url()
- xxx
url()
url()
函式在CSS中很常用,但是它的語法與其他函式不同,它可以接受帶引號的 url ,也可以接受不帶引號的 url。因為未加引號的 URL 不是有效的 SassScript 表示式,所以 Sass 需要特殊的邏輯來解析它。
如果 url()
的引數是一個有效的無引號的 url ,Sass 會原樣解析它,當然,插值也是可以用的。
如果引數不是一個有效的無引用的 url ,例如,如果它包含變數或函式呼叫,它將被解析為普通的 CSS 函式呼叫。
$roboto-font-path: "../fonts/roboto";
@font-face {
// This is parsed as a normal function call that takes a quoted string.
src: url("#{$roboto-font-path}/Roboto-Thin.woff2") format("woff2");
}
@font-face {
// This is parsed as a normal function call that takes an arithmetic
// expression.
src: url($roboto-font-path + "/Roboto-Light.woff2") format("woff2");
}
@font-face {
// This is parsed as an interpolated special function.
src: url(#{$roboto-font-path}/Roboto-Regular.woff2) format("woff2");
}
編譯後的 css :
@font-face {
src: url("../fonts/roboto/Roboto-Thin.woff2") format("woff2");
}
@font-face {
src: url("../fonts/roboto/Roboto-Light.woff2") format("woff2");
}
@font-face {
src: url(../fonts/roboto/Roboto-Regular.woff2) format("woff2");
}
calc()
和 element()
calc()
和 element()
函式是在 CSS 規範中定義的。因為 calc() 的數學表示式與 Sass 的演算法衝突,而 element()
的id可以被解析為顏色,所以它們需要特殊的解析。
Sass 允許任何文字出現在這些函式呼叫中,包括巢狀的圓括號。
除了可以使用插值來注入動態值會被編譯處理。其他任何東西都不會被解釋為 SassScript 表示式進行計算,而是原樣輸出。
progid:...()
和 expression()
棄用
expression()
和以 progid:
開頭的函式是使用非標準語法的 Internet Explorer 遺留特性。儘管最近的瀏覽器已經不再支援它們,但是 Sass 繼續解析它們以實現向後相容。
min()
和 max()
CSS在 CSS Values and Units Level 4
中增加了對 min()
和 max()
函式的支援,Safari 很快就採用了它們來支援 iPhoneX 。
但是 Sass 在很久以前就已經有了自己的 min()
和 max()
函式,為了向後相容所有現有的樣式表。這就需要額外的句法技巧來實現。
如果一個 min()
或 max()
函式呼叫是有效的純 CSS ,它將被編譯為普通的 CSS 的 min()
或 max()
函式呼叫。
"純CSS "包括巢狀呼叫 calc()
, env()
, var()
, min()
,或 max()
,以及插值。
但是,只要呼叫的時候包含了 SassScript 特性(如變數或函式呼叫),它就會被認為是對 Sass 自帶的 min()
或 max()
函式的呼叫。
變數
在 Sass 中,宣告變數必須以 $
開頭。
$red: #f00;
div {
color: $red;
}
編譯後的 css :
div {
color: #f00;
}
Sass 變數和 css 變數的區別:
-
Sass 變數會被編譯成真實的值然後輸出為 css ,也就是僅僅存在於開發階段。
-
CSS 變數對於不同的元素可以有不同的值,但是 Sass 變數一次只有一個值。
-
Sass 變數是不可逆的,這意味著如果您使用了一個變數,然後在後面更改了它的值,那麼之前的使用將依然保持不變。CSS 變數是宣告性的,這意味著如果在後面更改了值,它將影響前面的使用和以後的使用。
注意:和所有的 Sass 識別符號一樣,Sass 變數將連字元 -
和下劃線 _
視為相同的字元。這意味著 $font-size
和 $font_size
都指向同一個變數。這是 Sass 早期的歷史遺留,當時它只允許在識別符號名稱中使用下劃線。後來, Sass 增加了對連字元的支援,以匹配 CSS 的語法,sass 將這兩個字元視為等效處理,以便於使遷移更加容易。
預設值
比如開發一個庫,使用者可以選擇是否傳遞自定義的值,如果沒有傳遞則使用預設值。
為了實現這一點,Sass 提供了 !default
標誌。只有當變數沒有定義或者它的值為 null
時,才會給該變數賦值。否則,將使用預設的值。
配置模組變數
用 !default
定義的變數,可以在使用 @use
規則載入模組時配置。
在模組中宣告變數,並定義預設值:
// _library.scss
$black: #000 !default;
$border-radius: 0.25rem !default;
$box-shadow: 0 0.5rem 1rem rgba($black, 0.15) !default;
code {
border-radius: $border-radius;
box-shadow: $box-shadow;
}
在引用模組時,選擇要自定義值的變數,忽略的變數則使用預設值:
// index.scss
@use 'library' with (
$black: #222,
$border-radius: 0.1rem
);
內建變數
內建模組定義的變數是無法被修改的。
比如,下面程式碼檢視修改內建變數,但不會成功:
@use "sass:math" as math;
// This assignment will fail.
math.$pi: 0;
作用域
在 css 檔案頂層宣告的變數是全域性變數,聲明後可以在模組中的任何地方被訪問。
在塊({}
)中宣告的變數是區域性變數,只能在宣告它們的塊內訪問。
// 全域性變數
$red: #f00;
div {
// 區域性變數
$black: #000;
color: $red;
}
p {
// 在這裡引用區域性變數編譯時會報錯
color: $black;
}
當局部變數和全域性變數重名時,不會覆蓋全域性變數,而是同時存在,在哪個作用域訪問的就是哪個變數。
$red: #f00;
div {
$red: #f55;
color: $red;
}
p {
color: $red;
}
編譯後的 css :
div {
color: #f55;
}
p {
color: #f00;
}
如果想用一個區域性變數去覆蓋全域性變數,也就是在塊中修改全域性變數的值,可以使用 !global
來完成:
$red: #f00;
div {
// !global 將修改全域性變數的值,而不是在塊中新建一個區域性作用域
$red: #f55 !global;
color: $red;
}
p {
color: $red;
}
div {
color: #f55;
}
p {
color: #f55;
}
注意:如果使用 !global
的變數不是一個全域性變數,則編譯時會報錯。
在流程控制語句(@if/@each/@for/@while
等)中宣告的變數有一個自己的特殊作用域,它不會建立新變數去覆蓋同級作用域中的同名變數,而是簡單地進行原變數的賦值修改操作。
$dark-theme: true;
$red: #e55;
$black: #333;
@if $dark-theme {
$red: #f00;
$black: #000;
}
.button {
background-color: $red;
color: $black;
}
編譯後的 css :
.button {
background-color: #f00;
color: #000;
}
在流程控制語句中,賦值給已經存在的變數則是修改操作,如果是不存在的變數則會建立一個新的變數,但這個新的變數也只能在這個流程控制語句的作用域中使用。
檢測變數是否存在
Sass 核心庫提供了兩個用於處理變數的高階函式。meta.variable-exists()
函式返回給定名稱的變數是否在當前作用域中存在, meta.global-variable-exists()
函式做同樣的事情,但僅用於全域性作用域。
@debug meta.variable-exists("var1"); // false
$var1: value;
@debug meta.variable-exists("var1"); // true
h1 {
// $var2 is local.
$var2: value;
@debug meta.variable-exists("var2"); // true
}
@debug meta.global-variable-exists("var1"); // false
$var1: value;
@debug meta.global-variable-exists("var1"); // true
h1 {
// $var2 is local.
$var2: value;
@debug meta.global-variable-exists("var2"); // false
}
使用者有時可能會希望使用插值來定義基於另一個變數的變數名。Sass 不允許這樣做,因為它使得我們很難一眼就知道哪些變數在哪裡定義。但是,您可以做的是定義一個從名稱到值的 map
,然後您可以使用變數訪問該對映。
@use "sass:map";
$theme-colors: (
"success": #28a745,
"info": #17a2b8,
"warning": #ffc107,
);
.alert {
// Instead of $theme-color-#{warning}
background-color: map.get($theme-colors, "warning");
}
編譯後的 css :
.alert {
background-color: #ffc107;
}
插值(Interpolation)
插值幾乎可以在 Sass 樣式表的任何地方使用,以將 SassScript 表示式的結果嵌入到 CSS 塊中。
在 #{}
中放置一個表示式即可,比如可以用在:
- 選擇器
- 屬性名
- 自定義屬性值
- CSS 的
@
語句中 @extends
- CSS
@imports
- 字串
- 特殊函式
- CSS 函式名
- 保留註釋(Loud comments )
/* ... */
下面展示部分用法,在選擇器,屬性,繼承,註釋語句中使用插值:
$selector: "hello";
$color: "color";
/* selector: #{$selector} */
.#{$selector} {
background-#{$color}: #f00;
}
.#{$selector}-2 {
@extend .#{$selector};
border-#{$color}: #f00;
}
/* selector: hello */
.hello,
.hello-2 {
background-color: #f00;
}
.hello-2 {
border-color: #f00;
}
在 SassScript 中,可以使用插值表示式將 SassScript 注入到未加引號的字串中。這在動態生成名稱(例如動畫)或使用斜槓分隔值時特別有用。
注意: SassScript 中的插值永遠返回一個未加引號的字串,在上面的例子中已經看到了。
插值對於將值注入到字串中很有用,但除此之外,在 SassScript 表示式中很少需要插值。
比如,使用變數完全不需要這樣寫: color: #{$red}
,而是可以直接使用變數: color: $red
。
注意:不應該使用插值插入數字。因為插值總是返回未加引號的字串,返回值並不能進一步用於計算,這也同時避免了違反 Sass 內建的的安全保護規則,以確保能夠正確使用單位。
Sass 有強大的單位運算,你可以使用它來代替。例如,與其寫 #{$width}px
,不如寫 $width * 1px
,或者更好的是,以px開頭宣告$width變數。這樣,$width
已經有單位,你將得到一個很好的錯誤訊息,而不是編譯偽造的CSS。
還有,雖然可以使用插值將帶引號的字串轉換為不帶引號的字串,但使用 string.unquote()
函式會更清楚。所以應該用 string.unquote($string)
來代替 #{$string}
。
@語句(At-Rules)
Sass 在 CSS 之上添加了新的 @
語句 :
-
@mixin
和@include
用於複用大的塊級樣式 -
@function
宣告自定義函式,用於 SassScript 表示式中 -
@extend
用於在一個選擇器中繼承另一個選擇器的樣式 -
@at-root
將程式碼塊內部的樣式編譯到 css 最外層(相當於頂級作用域) -
@error
故意使編譯失敗而中斷,並丟擲錯誤資訊 -
@warn
丟擲一條錯誤資訊但不使編譯程式失敗而中斷 -
@debug
丟擲一條用於 debug 除錯的訊息 -
@if
,@each
,@for
,@while
流程控制語句
@mixin
and @include
@mixin
用於定義要複用的樣式塊,@include
用於呼叫這些樣式塊。
因歷史遺留原因,mixin 的名字和 Sass 識別符號一樣,連字元(hyphens) -
和下劃線(underscores)_
被視為完全相同。
定義 mixin 的語法:
// 不需要傳引數時,複用固定的樣式程式碼
@mixin <name> {
// ...
}
// 或
// 需要使用時傳遞引數,動態複用樣式程式碼
@mixin name(arg1, arg2, ..., argN) {
// ...
}
使用 mixin 的語法:
@include <name>;
// 或
@include <name>(arg1, arg2, ...)
使用示例:
// a.scss
@mixin input {
padding: 10px;
color: #333;
}
@mixin button ($color, $fontSize) {
color: $color;
font-size: $fontSize;
}
@use "a";
.input {
@include a.input;
}
.button {
@include a.button(#333, 20px);
}
編譯後的 css :
.input {
padding: 10px;
color: #333;
}
.button {
color: #333;
font-size: 20px;
}
通常情況下,如果一個 mixin 定義時有多少個引數,那麼在呼叫時必須傳遞相同數量的引數,除非是定義 mixin 時使用了引數預設值。
mixin 引數預設值
定義一個引數預設值就像定義一個變數一樣,引數名後加一個冒號,然後就可以寫預設值了。
@mixin button ($color, $fontSize: 16px) {
color: $color;
font-size: $fontSize;
}
.button {
@include button(#f00);
}
編譯後的 css :
.button {
color: #f00;
font-size: 16px;
}
預設引數值可以是任意 Sass 表示式,甚至是它前面的引數。
@mixin font ($size, $weight: if($size >= 24px, 600, 400)) {
font-size: $size;
font-weight: $weight;
}
.div1 {
@include font(16px);
}
.div2 {
@include font(24px);
}
編譯後的 css :
.div1 {
font-size: 16px;
font-weight: 400;
}
.div2 {
font-size: 24px;
font-weight: 600;
}
關鍵詞傳參
預設情況下,呼叫 mixin 時傳遞的引數順序必須和定義時的引數一一對應。
如果傳遞引數時指定引數關鍵詞,則可以不按照定義的順序來傳參。
@mixin font ($weight, $size) {
font-size: $size;
font-weight: $weight;
}
.div1 {
@include font($size: 16px, $weight: 500);
}
編譯後的 css :
.div1 {
font-size: 16px;
font-weight: 500;
}
注意,如果要傳遞不帶關鍵詞的引數,則它必須出現在關鍵詞引數之前。
任意數量的引數
如果 mixin 的最後一個引數名以 ...
結尾,那麼這個引數就可以接收傳遞過來的任意數量的引數,這個引數的值則會是一個列表。
@mixin order($height, $selectors...) {
@for $i from 0 to length($selectors) {
#{nth($selectors, $i + 1)} {
position: absolute;
height: $height;
margin-top: $i * $height;
}
}
}
@include order(150px, "input.name", "input.address", "input.zip");
編譯後的 css :
input.name {
position: absolute;
height: 150px;
margin-top: 0;
}
input.address {
position: absolute;
height: 150px;
margin-top: 150px;
}
input.zip {
position: absolute;
height: 150px;
margin-top: 300px;
}
帶有關鍵字的任意引數
如果呼叫 mixin 帶了關鍵字,那麼任意引數需要使用 meta.keywords()
來處理,處理後將返回一個 map 型別的資料。
如果沒有將任意引數傳遞給 meta.keywords()
函式,那麼這個任意引數列表就不允許接收帶有關鍵詞的引數,編譯程式會報錯。
@use "sass:meta";
@mixin syntax-colors($args...) {
@debug meta.keywords($args);
// (string: #080, comment: #800, variable: #60b)
@each $name, $color in meta.keywords($args) {
pre span.stx-#{$name} {
color: $color;
}
}
}
@include syntax-colors(
$string: #080,
$comment: #800,
$variable: #60b,
)
pre span.stx-string {
color: #080;
}
pre span.stx-comment {
color: #800;
}
pre span.stx-variable {
color: #60b;
}
傳遞任意引數
接收的任意引數可以是一個列表(list),那麼,也可以把一個列表作為任意引數傳遞,同樣只需要在後面加上 ...
即可。
$font: 16px, 600, #f00;
@include font($font...);
同樣,也可以把一個 map
作為任意引數傳遞:
@mixin font ($size, $weight) {
font-size: $size;
font-weight: $weight;
}
$font: (
weight: 600,
size: 16px,
);
.div1 {
@include font($font...);
}
編譯後的 css :
.div1 {
font-size: 16px;
font-weight: 600;
}
@content
樣式塊
除了接受引數之外,mixin 還可以接受整個樣式塊,稱為內容塊。
在 mixin 中,在樣式塊中寫一個 @content
來宣告這個位置接受一個內容塊,傳遞一個樣式塊給 mixin,這個樣式塊的內容將會用來替換 @content
。
@mixin font ($size, $weight) {
font-size: $size;
font-weight: $weight;
@content;
}
$font: (
weight: 600,
size: 16px,
);
.div1 {
@include font($font...) {
font-family: sans-serif;
}
}
編譯後的 css :
.div1 {
font-size: 16px;
font-weight: 600;
font-family: sans-serif;
}
可以書寫多個 @content;
,這樣將會編譯生成多個接收到的樣式塊。
傳遞的樣式塊是有作用域限制的,只能訪問樣式塊所處的位置的變數,而不能去訪問 mixin 定義的作用域的變數。
如果要讓樣式塊使用 mixin 定義的作用域的變數,則需要通過 @content()
傳遞給樣式塊。
使用 `@content 時傳參
傳參使用 @content(arg1, arg2, ...)
,接收使用 @include <name> using ($arg1, $arg2, ...)
@mixin font ($size, $weight) {
font-size: $size;
font-weight: $weight;
@content(#f00, $size * 2);
}
$font: (
weight: 600,
size: 16px,
);
.div1 {
@include font($font...) using ($color, $margin) {
font-family: sans-serif;
color: $color;
margin: $margin;
}
}
編譯後的 css :
.div1 {
font-size: 16px;
font-weight: 600;
font-family: sans-serif;
color: #f00;
margin: 32px;
}
@content()
同樣可以傳遞 list
或 map
型別的引數,用法和前面一樣。
縮排語法的 mixin
縮排語法的 Sass 可以使用 =
來定義一個mixin,然後使用 +
來使用一個 mixin,但很不直觀,不建議使用。
@at-root
通常用於巢狀的選擇器中,在選擇器前寫下 @at-root
語句,用於將該選擇器編譯到樣式表的最外層,而不是巢狀所在的位置。
.div1 {
color: #f00;
.div2 {
color: #0f0;
// 將 .div3 編譯到最外層
@at-root .div3 {
color: #00f;
}
}
}
編譯後的 css :
.div1 {
color: #f00;
}
.div1 .div2 {
color: #0f0;
}
.div3 {
color: #00f;
}
結合 mixin 來使用:
@use "sass:selector";
@mixin unify-parent($child) {
@at-root #{selector.unify(&, $child)} {
font-size: 16px;
@content;
}
}
.wrapper .field {
@include unify-parent("input") {
color: #f00;
}
@include unify-parent("select") {
color: #0f0;
}
}
編譯後的 css :
.wrapper input.field {
font-size: 16px;
color: #f00;
}
.wrapper select.field {
font-size: 16px;
color: #0f0;
}
@at-root
還有另一種寫法 @at-root { ... }
:
.div1 {
font-size: 16px;
@at-root {
.div2 {
color: #f00;
}
.div3 {
color: #0f0;
}
}
}
編譯後的 css :
.div1 {
font-size: 16px;
}
.div2 {
color: #f00;
}
.div3 {
color: #0f0;
}
解決樣式之外的東西
預設情況下, @at-root
只會解決普通樣式規則, 其他像是 @media
或 @supports
等將會被丟掉。
使用 @at-root (with: <rules...>) { ... }
或 @at-root (without: <rules...>)
來告訴 Sass 在編譯的時候是否包括一些指定的規則。
除了合法的 @
語句的名稱,如 @media
中的 media
,還有兩個特殊的值可以在查詢中使用:
rule
指的是樣式規則。例如,@at-root (with: rule)
不保留 @ 語句,但保留樣式規則。all
指所有 @語句 和 style 規則。
@media screen and (min-width: 900px) {
.page {
width: 100px;
@at-root (with: media) {
/* @at-root (with: media) */
.div1 {
font-size: 16px;
}
}
@at-root (without: media) {
.div2 {
/* @at-root (without: media) */
color: #111;
}
}
@at-root (with: rule) {
.div3 {
/* @at-root (with: rule) */
color: #111;
}
}
@at-root (without: rule) {
.div4 {
/* @at-root (without: rule) */
color: #111;
}
}
@at-root (with: all) {
.div5 {
/* @at-root (with: all) */
color: #111;
}
}
@at-root (without: all) {
.div6 {
/* @at-root (without: all) */
color: #111;
}
}
}
}
編譯後的 css :
@media screen and (min-width: 900px) {
.page {
width: 100px;
}
/* @at-root (with: media) */
.div1 {
font-size: 16px;
}
}
.page .div2 {
/* @at-root (without: media) */
color: #111;
}
.page .div3 {
/* @at-root (with: rule) */
color: #111;
}
@media screen and (min-width: 900px) {
.div4 {
/* @at-root (without: rule) */
color: #111;
}
}
@media screen and (min-width: 900px) {
.page .div5 {
/* @at-root (with: all) */
color: #111;
}
}
.div6 {
/* @at-root (without: all) */
color: #111;
}