CSS變數的作用域是什麼?
變數作用域,變數的可用性範圍。變數並不總是有效可用的,而限定變數的可用性範圍就是變數的作用域。而CSS變數在CSS層次結構中宣告的位置,決定了它在整個層次結構中的可用性範圍。
通常來說,CSS變數僅對宣告它的元素的子元素可見。比如下面的例子中,--bgColor
變數對child元素可見:
<div class="parent">
parent
<div class="child">child</div>
</div>
.parent {
--bgColor: pink;
}
.child {
background: var(--bgColor);
}
但如果反過來則不可見:
.child {
--bgColor: pink;
}
.parent {
background: var(--bgColor);
}
作用域型別
CSS變數遵循詞法作用域(靜態作用域)規則,有兩種作用域型別——全域性作用域和區域性作用域。
全域性作用域
在:root
中宣告的CSS變數即全域性作用域的變數,即可以在CSSOM中任意位置使用。
/* 定義全域性變數 */
:root{
--primary-color: pink;
}
/* 任意位置都可以訪問全域性變數 */
.wrapper{
background: var(--primary-color);
}
區域性作用域
而在其他CSS層級中宣告的變數僅對該CSS層級以及它的子級可見。
<div class="parent text">
parent
<div class="child text">child</div>
</div>
.parent {
--fontSize: 24px;
--lineHeight: 1.8;
}
.child {
--fontSize: 18px;
--lineHeight: 1.6;
}
.text {
font-size: var(--fontSize);
line-height: var(--lineHeight);
}
在上面對例子中,可以用相同的命名在不同的CSS塊中宣告和訪問變數。
區域性作用域總是可以訪問外層作用域或者全域性作用域的變數,相反則不可。
變數提升
和JavaScript一樣,CSS變數生命可以被提升,即CSS變數可以再宣告之前使用他們。在瀏覽器渲染相應的HTML元素樣式前,會將CSS變數的宣告提升並移動到CSSOM的最頂部。
body {
background-color: var(--bgColor);
}
:root {
--bgColor: pink;
}
body {
background-color: var(--bgColor);
}
:root {
--bgColor: pink;
}
如上面的例子中,CSS變數--bgColor
可以在:root
偽類選擇器宣告之前使用。CSS變數可以先訪問再宣告這一特性,使得CSS變數成為一個非常強大的功能點。
變數預設值
如下面的例子中,被逗號分隔後的第二個值1.2為預設值,即如果--scale
未被賦值則使用1.2這個數值。
.bigger {
transform: scale(var(--scale, 1.2));
}
當使用var()
函式時,可以分配一個或多個回退的屬性值(使用逗號分隔),比如設定字型:
html {
font-family: var(--fonts, Helvetica, Arial, sans-serif);
}
還可以使用一連串的變數回退,但需要使用var()
巢狀起來:
.bigger {
transform: scale(var(--scale, var(--secondFallbackScale, 1.2));
}
如果--scale
未被定義,會嘗試下一個值即--secondFallbackScale
。如果--secondFallbackScale
也未被定義,最終會回退到1.2。
模組化
CSS變數的強大之處在於,作用域的特性有助於設計一個程式碼整潔、模組化的系統。
當我們想要主題化某一模組時,可以在該模組的根元素中設定CSS變數,以便於變數可以向下傳遞,而不影響該模組以外的元素。
<html>
<body>
<div class="mod">
<p>
段落 1
</p>
</div>
<p>
段落 2
</p>
</body>
</html>
.mod {
--modBgColor: pink;
--modMainColor: blueviolet;
}
p {
background: var(--modBgColor);
color: var(--modMainColor);
}
在.mod
中宣告的CSS變數將對<p>
元素的“段落 1”可見,它是類名是mod
節點的子元素,因此設定的背景色和字型色會對“段落 1”生效。
而<p>
元素的“段落2”樣式不會受到影響,因為它不屬於.mod
或其子元素,CSS變數--modBgColor
和--modMainColor
對它不可見。
這就是CSS變數作用域的強大之處。
變數賦值變數
CSS變數也可以使用變數賦值變數,下面的例子中,使用CSS變數渲染漸變背景色:
<div class="gradient"></div>
<div class="gradient"></div>
:root {
--color1: pink;
--color2: aquamarine;
--bg: linear-gradient(to right, var(--color1), var(--color2));
}
.gradient {
margin: 10px auto;
width: 200px;
height: 100px;
background: var(--bg);
}
變數不生效問題
現在,如果想讓第二個模組漸變色變成從粉色過渡到薰衣草色,那就簡單粗暴地給第二個模組加個類名theme-2
,同時給變數—color2
賦值薰衣草色:
.theme-2 {
--color2: lavender;
}
然而變數並沒有生效,問題在於--bg
是在:root
中被宣告且被賦值了紅到綠的漸變值,可以使用這個變數是因為它是全域性變數可訪問,在theme-2
中修改的—-color2
的值並不會更新—-bg
的值,轉換成js語句就容易理解了:
let color1 = 'red';
let color2 = 'aquamarine';
let bg = `${color1}-${color2}`;
function gradient() {
color2 = 'lavender';
console.log(bg);
}
gradient(); // red-aquamarine
解決方法:在使用變數的層級賦值
:root {
--color1: pink;
--color2: aquamarine;
}
.gradient {
--bg: linear-gradient(to right, var(--color1), var(--color2));
background: var(--bg);
}
.theme-2 {
--color2: lavender;
}
把—-bg
變數的賦值放到.gradient
層級中,—-color2
變數就生效了:
需要合理使用
:root {
--prop1: lol;
--prop2: var(--prop1) var(--prop1);
--prop3: var(--prop2) var(--prop2);
--prop4: var(--prop3) var(--prop3);
...
--prop30: var(--prop29) var(--prop29);
}
如果無限制地渲染和執行,上面的程式碼段會導致瀏覽器嘗試建立大約十億個—-prop1
值('lol')的例項,這足以讓大多數系統記憶體不足。這個例子在2019年的時候使用32gb RAM的MacBook Pro進行測試,30秒執行後Safari停止響應。那時候所有WebKit核心的瀏覽器都容易通過CSS變數受到攻擊,現在的瀏覽器已對該情況做相應的處理,被變數引用賦值超過65536次的變數會被當作無效值處理。
實戰演練(舉個?)
我們可以在不同場景下妙用CSS變數的作用域這一特性,以下例子中演示瞭如何使用CSS變數實現一個可切換主題的開關。
https://codepen.io/bobolovecat/pen/vYZJPop
我們建立了2個作用域範圍,分別是代表明亮主題的.theme-bright
和暗黑主題的.theme-dark
。在這兩個區域性作用域中使用相同的變數名,各個變數只對主題相應的模組生效。
小結
利用CSS變數的作用域特性和var()的變數回退,有助於我們設計一個程式碼整潔、模組化的系統。但也要注意作用域層級和合理運用CSS變數賦值,避免程式碼結構過於複雜以及影響頁面渲染效能。
參考
The Big Gotcha With Custom Properties
A Complete Guide to Custom Properties
Understanding the Scope in CSS Variables - Part 2 - Webkul Blog