详解CSS变量的作用域和默认值
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; }
如上面的例子中,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变量实现一个可切换主题的开关。
<template> <div :class="`theme-${theme}`"> <a class="switch" @click="theme = theme === 'dark' ? 'bright' : 'dark'">{{ theme === 'dark' ? '明亮主题' : '暗黑主题' }}</a> <header> <h1>这是头部</h1> </header> <p>这是内容</p> <footer> <h1>这是底部</h1> </footer> </div> </template> <script> export default { data() { return { theme: 'bright', }; }, methods: { doSomething() { alert("Hello!"); } } }; </script> <!-- Use preprocessors via the lang attribute! e.g. <style lang="scss"> --> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; text-align: center; color: #2c3e50; margin-top: 60px; } * { margin: 0; } .theme-bright { --bg-fill: #ffeaea; --text-color: #060130; } .theme-dark { --bg-fill: #060130; --text-color: #ffeaea; } header, footer { background: var(--bg-fill); color: var(--text-color); } p { width: 800px; height: 500px; margin: 40px auto; padding: 15px; max-width: 80%; line-height: 1.6; font-size: 15px; background: var(--bg-fill); color: var(--text-color); } h1 { text-align: center; margin: 0; padding: 10px; } .switch { background: var(--text-color); padding: 5px 15px 6px 15px; position: fixed; right: 13px; top: 13px; color: var(--bg-fill); border-radius: 3px; cursor: pointer; } </style>
效果如下:
我们创建了2个作用域范围,分别是代表明亮主题的.theme-bright
和暗黑主题的.theme-dark
。在这两个局部作用域中使用相同的变量名,各个变量只对主题相应的模块生效。
总结
利用CSS变量的作用域特性和var()
的变量回退,有助于我们设计一个代码整洁、模块化的系统。但也要注意作用域层级和合理运用CSS变量赋值,避免代码结构过于复杂以及影响页面渲染性能。
相关文章推荐:
1. 本站所有文章教程及资源素材均来源于网络与用户分享或为本站原创,仅限用于学习和研究。
2. 如果内容损害你的权益请联系客服QQ:1642748312给予处理。
码云笔记 » 详解CSS变量的作用域和默认值