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