详解CSS变量的作用域和默认值

目录
文章目录隐藏
  1. CSS 变量的作用域是什么?
  2. 作用域类型
  3. 变量提升
  4. 变量默认值
  5. 模块化
  6. 变量赋值变量
  7. 实战演练
  8. 总结

CSS 变量的作用域是什么?

变量作用域,变量的可用性范围。变量并不总是有效可用的,而限定变量的可用性范围就是变量的作用域。而 CSS 变量在 CSS 层次结构中声明的位置,决定了它在整个层次结构中的可用性范围。

通常来说,CSS 变量仅对声明它的元素的子元素可见。比如下面的例子中,--bgColor变量对 child 元素可见:

<div class="parent">
  parent
  <div class="child">child</div>
</div>
.parent {
    --bgColor: pink;
}
.child {
    background: var(--bgColor);
}

CSS 变量的作用域是什么?

但如果反过来则不可见:

.child {
    --bgColor: pink;
}
.parent {
    background: var(--bgColor);
}

CSS 变量的作用域是什么?

作用域类型

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>

效果如下:

如何使用 CSS 变量实现一个可切换主题的开关

我们创建了 2 个作用域范围,分别是代表明亮主题的.theme-bright和暗黑主题的.theme-dark。在这两个局部作用域中使用相同的变量名,各个变量只对主题相应的模块生效。

总结

利用CSS 变量的作用域特性和var()的变量回退,有助于我们设计一个代码整洁、模块化的系统。但也要注意作用域层级和合理运用 CSS 变量赋值,避免代码结构过于复杂以及影响页面渲染性能。

相关文章推荐:

「点点赞赏,手留余香」

0

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
码云笔记 » 详解CSS变量的作用域和默认值

发表回复