Sass 巢狀規則:依結構縮排書寫,與過深的代價
巢狀(Nesting)是最直覺、也最容易被濫用的 Sass 功能。它讓選取器可以依照 HTML 的結構層層寫進去,不必每一條規則都重複前綴。
選取器巢狀
.card {
padding: 16px;
.title {
font-size: 20px;
}
.content {
color: #666;
}
}編譯後會攤平成 後代選取器:
.card {
padding: 16px;
}
.card .title {
font-size: 20px;
}
.card .content {
color: #666;
}關鍵在「攤平」這兩個字。 縮排只是書寫方式,產出的仍是一條條完整的選取器,你寫了幾層,選取器就有多長。
巢狀裡的其他選取器
.list {
> li {
padding: 8px 0;
}
+ .list {
margin-top: 24px;
}
}.list > li {
padding: 8px 0;
}
.list + .list {
margin-top: 24px;
}至於偽類、修飾類別這種要「接在父選取器後面、中間不能有空白」的情況,得用 &,見 父選取器。
媒體查詢的巢狀
@media 寫在規則內部,Sass 會自動把它提到外層:
.card {
padding: 16px;
@media (min-width: 768px) {
padding: 32px;
}
}.card {
padding: 16px;
}
@media (min-width: 768px) {
.card {
padding: 32px;
}
}這是巢狀最有價值的用途
斷點樣式就寫在它修改的那個屬性旁邊,改一個元件不必在檔案的兩端來回跳。斷點值集中管理的做法見 斷點管理。
屬性巢狀
有共同前綴的屬性也能巢狀:
.title {
font: {
family: "Noto Sans TC", sans-serif;
size: 20px;
weight: 700;
}
}.title {
font-family: "Noto Sans TC", sans-serif;
font-size: 20px;
font-weight: 700;
}注意 font: 後面的冒號不能省。要同時給簡寫與細項的話:
.box {
margin: auto {
bottom: 16px;
}
}這個功能實務上很少用
省下的字不多,可讀性反而下降,而且搜尋 font-size 時找不到它。知道有這回事就好。
三層是那條線
巢狀的代價全在編譯後的產出上:
.page {
.sidebar {
.menu {
.item {
.link { color: #333; }
}
}
}
}.page .sidebar .menu .item .link {
color: #333;
}這一條規則有三個問題:
| 問題 | 說明 |
|---|---|
| 優先權 過高 | 五個類別 = 0,5,0,想覆蓋它得寫更長的選取器 |
| 檔案變大 | 每條規則都帶著完整祖先路徑 |
| 綁死結構 | HTML 少一層 <div>,樣式就失效 |
改寫成 BEM:
.menu {
&__item { /* ... */ }
&__link { color: #333; }
}.menu__item { /* ... */ }
.menu__link { color: #333; }優先權只有 0,1,0,也不再依賴 DOM 有幾層。
常見問題
巢狀最多可以幾層?
語法上沒有限制,但實務上建議不超過三層。每多一層,產出的選取器就多一段、優先權 就更高,之後要覆蓋它就得寫出更長的選取器,最後演變成到處加 !important。
巢狀會讓 CSS 檔案變大嗎?
會。巢狀在編譯時是攤平成完整選取器,三層巢狀的每一條規則都帶著完整的祖先路徑,重複的前綴字串會實際佔用檔案體積,也讓瀏覽器比對選取器的成本上升。
媒體查詢可以寫在巢狀裡嗎?
可以,而且很推薦。把 @media 寫在元件的規則內部,Sass 編譯時會自動把它提到外層並補上完整選取器,好處是斷點的樣式就寫在它修改的那個屬性旁邊,不必在檔案兩端來回對照。
屬性巢狀是什麼?
有共同前綴的屬性可以用巢狀寫,例如把 font-family、font-size 寫成 font: 加一個巢狀區塊。它能減少重複,但可讀性見仁見智,實務上使用得不多。