巧妙的 CSS 变量
在 CSS 中使用变量,是长期以来所有前端伙伴们的急迫诉求。 通过构建工具,我们已经可以在 Sass 和 Less 中愉快地使用变量。 那么 CSS 变量,又能给我们带来什么呢?仅仅是跟着走 Sass/Less 所走过的路吗? 不,作为标准的亲儿子,CSS 变量有着无可比拟的优越性。
动态性
Sass/Less 中的变量,在项目构建之后,被常量所替代。 即,我们不可能通过客户端 JS 去访问,或动态地修改这些变量。它们归根结底,是静态的。 而 CSS 变量,是动态的。@media 和 JS 可以动态地修改这些变量,而引用它们的地方,将同步改变。
更好的作用域规则
CSS 变量,又称为“自定义属性”。从作用域的角度看,它不像 Sass/Less 的变量,而像普通的 CSS 属性。
Sass/Less 变量的作用域就是语法上的,也就是一对{}
之间。
而 CSS 变量,则和一般属性一样,通过选择器,绑定到 DOM 之上,并在 DOM 节点和其字节点中有效。
Sass/Less 变量的作用域取决于你怎么写,CSS 变量作用域取决于最终的 DOM 树。
这有什么好处呢?别急,先来看一个例子。
组件化思想与自定义属性
这里我们给 button
设置尺寸,依据是变量 --button-size
的值,若不存在,则取缺省值为 1
。
这个规则,将应用于页面上所有的 <button>
上。
又在 body>main
上设置了 --button-size: 1.5
。规则作用于 <body>
下,直属的 <main>
上。
容易预见,所有这个 <main>
中的 <button>
的高度为 1.5rem
,而外部的 <button>
则高度为 1rem
。
写 C 的伙伴:不科学!变量怎么能使用在先,定义在后?怎么能取到别的作用域下的变量?
让我们换个角度来理解问题,把 button
部分看作是函数,而将其中的 --button-size
看作参数(形参)。
这个“函数”将在 <button>
被渲染时调用。而此时,body>main
上所赋予的值,将作为参数(实参)传入。
这个小例子体现出了封装,以及组件化的思想。 button 是这样一个组件,有着内部的样式,又向外暴露若干参数接口。可在各种场景中复用。 而外界不对这个组件的普通样式进行入侵,只修改其所暴露的参数。 此时,CSS 变量,就好似组件的自定义样式属性。
至此,还不是真正的组件化,我们必须以规范来避免外界的样式入侵。一旦不小心破坏规则,一切将乱套。 所幸,我们已经有 Web Components。
结合 Web Components
Custom Elements 中,组件里的样式表与外界几乎是隔离的。但 CSS 变量却可以穿透这个屏障。是十分便利的通信渠道。 这是一个自定义元素内部 CSS 的例子。
此例中,我们使用变量给自定义元素内部的 button 定义样式。与前面的例子是一样的。
值得注意的是,我们在 :host
上声明了 --button-size
,而没有声明 --font-size
。
造成的区别就是:任何外层元素上定义的 --font-size
将对自定义元素内的 <button>
产生影响;
而外部要想设置 --button-size
的值,必须直接定义在这个自定义元素上。
两种方式,各有其适用的场景。
--button-size
的方式,适用于一般情况下组件接受外部参数。参数必须明确地直接传递给组件,避免混乱。
而 --font-size
的方式,适用于读取全局或局部的配置,不可滥用。
与 JS 的交互
以下是 JS 操作样式的接口,可以操作普通的样式属性,以及自定义属性(CSS 变量)。
即便是普通 CSS 属性,这也是更好的操作方式。推荐统一使用这套接口操作样式。
有一种场景是,JS 捕获用户事件后,获取事件参数,例如鼠标的位置;并读取一些布局信息, 例如某个 DOM 的宽高;再经过一系列计算后,将几个计算结果设置到该 DOM 的 style 上。 这样,JS 就干涉了布局,与 CSS 强耦合,违背了关注点分离。 由于存在对布局信息的读取,会引起额外的重绘。
使用 CSS 变量,则完美地解决了这个痛点。JS 中你只需将鼠标的位置传递给 CSS 变量,
而无需注意其它细节。在 CSS 中通过 calc()
完成计算和布局。
很好地遵循了关注点分离,也避免了额外的重绘。
数据类型
在 CSS 中,基本数据类型有数量,字符串,关键字,颜色等。此外,还有序列。 这些类型的数据,都可以作为 CSS 变量的值。
使用时,可以自由组合,计算。计算时,要注意结果的单位。
而序列的组合,只要把 ,
和 /
等符号,视为与 1px
等普通值一样的序列元素,便能理解其行为了。
结合过渡与动画
很遗憾,尚无浏览器支持 CSS 变量的过渡效果。只能对常规属性使用过渡效果或关键帧。
因此,有许多很棒的想法还不能实现。譬如色相与亮度以不同的速度变化,例如渐变背景的颜色控制等。期待未来的发展吧。