mengtuifrontend / Blog

芦叶满汀洲,寒沙带浅流。二十年重过南楼。柳下系船犹未稳,能几日,又中秋。 黄鹤断矶头,故人今在否?旧江山浑是新愁。欲买桂花同载酒,终不似,少年游。
18 stars 5 forks source link

了解 CSS 变量 #7

Open mengtuifrontend opened 5 years ago

mengtuifrontend commented 5 years ago

了解 CSS 变量

如果用过 less\scss\stylus 等预处理 CSS 语言,那么你就不会对使用变量来简化我们的 CSS 开发工作感到陌生。但是你知道吗?CSS 现在也支持原生的变量了: var()

基本用法

CSS 原生的变量如何使用?我们来看下:

.selector {
  --size: 16px;
 font-size: var(--size)
}

这个简单的例子中,.selectorfontSize 值就是 16px。但是这么简单的例子凸显不出 CSS 变量的价值,我们来看个更能体现变量价值的例子:

body {--bg: #fff}
p {background: var(--bg)}
a {color: var(--bg)}
span {border: 1px solid var(--bg)}

这个例子中,我们在 body 元素中定义了变量 --bg,并在其他三个标签上应用了变量,而且用于不同的 CSS 属性。变量无疑给我们开发 CSS 带来了便利和更好的维护性,简单修改变量值即可同时在不同 selector 和不同属性上生效。那么我们接下来再仔细看看 CSS 变量的一些特性。

变量声明

--* 格式

CSS 变量有一个很明显的特点就是必须以 -- 起始,看起来很奇怪。早期的规范是以 var- 作为起始,所以在一些老版本浏览器中可能需要定义 var- 起始(firefox 31 以下 bug 985838)的变量名才能生效。

我个人感觉有一个好处就是官方钦定了 CSS 变量名的烤肉串风格(Kebabs Style)写法(人都给你两个 - 了)。

大小写敏感

和普通的 CSS 属性忽略大小写不同,变量名对大小写是敏感的。

body {
  --color: #f90;
  --Color: #f00;
  background: var(--color); /* #f90 */
}

建议变量名全小写,原因就是上面我们提到的,变量名使用烤肉串风格声明。

变量必须声明在样式规则中

不同于预处理语法直接声明变量,CSS 变量必须声明在样式规则中,包括条件化规则 @media 等。

--size: 20px; /* 语法错误 */

body {
  --size: 20px; /* 正确声明 */
}

但是在 @keyframes 中定义的变量会被作为动画属性。因为规范规定变量是 Animatable: no 的,不可以作为动画属性的。一旦在 @keyframes 中定义了变量,且有动画属性使用了该变量,那么这个属性将会受到影响,导致动画失效。

@keyframe test {
 from { --color: #f00; background: var(--color)}
 to { --color: #fff; background: var(--color)}
}

这种方式的写法,背景色不会出现变化哦。那该怎么做呢?一种方式就是多定义几个变量,比如:

@keyframe test {
 from { background: var(--color-start)}
 to { background: var(--color-end)}
}

引用变量

变量必须通过将变量名放入 var() 中进行引用,否则会被忽略。

body {
  --color: #f90;
  background: --color; /* 语法错误 */
  background: var(--color) /* 正确 */
}

继承与级联优先级

CSS 变量也遵循 CSS 的继承规则和级联优先级规则。比如,当一个规则使用了变量,但是自身没有定义该变量时,CSS 解析器会向上查找变量,试图使用父级、祖父级的变量。

.parent {--size: 20px}
.parent .current {font-size: var(--size)} /* current 的规则并没有定义 --szie 变量,使用的是继承到的变量 */

当多条规则中有重复定义的变量时,解析器会按样式级联优先级来确定使用哪个值:

.current.more {--size: 20px;} /* 这条规则权重大,所以 --size 变量取值为 20px */
.current {--size: 10px}

注意没有继承关系时,可以存在多个同名变量。

<div class="one"></div>
<div class="two"></div>
<div class="three"></div>
.one {--size: 10px; font-size: var(--size)}
.two {--size: 20px; font-size: var(--size)}
.three {--size: 30px; font-size: var(--size)}

/*等同于*/
.one {font-size: 10px}
.two {font-size: 20px}
.three {font-size: 30px}

默认值

如果需要默认值,可以在 var 方法中传入哦。

body {background: var(--bg, #f00)}

如果没有找到 --bg 变量,那么 #f00 会生效。

需要注意的是,在使用变量时可能出现非法值的情况:

body {--bg: 20px; background: var(--bg, #f00)}

这种情况下,规则会被解析为:

body {background: 20px}

最终 body 的背景色是透明,而不是我们在 var 中设置的默认值。也就是说,CSS 变量的默认值只在变量未声明的情况下生效,不会影响值与属性非法组合情况。

变量提升

和 JS 中定义变量类似,CSS 变量也拥有变量提升的效果,但是也有差异。

console.log(a) // undefined
var a = 1;
console.log(a) // 1

//等同于
var a;
console.log(a)
a = 1;
console.log(a)

JS 中变量只有声明会被提升,值还是按照正常的代码流程进行赋值。而 CSS 变量不仅声明被提升,值也会被提升。

body {background: var(--color); --color: #f00}

/* 等同于 */
body {--color: #f00; background: var(--color)}

在定义变量之前使用变量,和定义变量之后使用变量效果相同。

变量赋值

上面的例子中我们都在 CSS 属性值的位置使用变量,那么我们可以将变量作为 CSS 属性吗?比如:

--prop : font-size;
var(--prop): 12px

答案是:不可以。

虽然CSS 变量不能作为属性名,但是它可以使用另一个变量进行赋值或表达式计算:

body {
  --size: 10px;
  --big-size: calc(var(--size) * 2);
}

此处,--big-size 变量的值为 20px

赋值的时候我们需要注意给变量带上单位,如果在引用之后加上单位是不能正确解析的,如:

body {
  --size: 10;
  font-size: var(--size)px; /* 会被解析成 font-size: 10 px,数字 10 与 px 之间有一个空格  */
}

这种写法等同于给 CSS 属性设置了一个非法值。

CSSOM 进行动态设置 CSS 变量

规范中有提到可以使用 CSSOM 进行变量的获取与设置:

element.style.setProperty('--foo', '10px')
element.style.setProperty('height', 'var(--foo)') // 设置元素 height 为 10px
element.style.getPropertyValue('--foo') // 获取变量值,返回 10px

我们可以通过 DOM 对象的 style 属性进行 CSS 变量的取值和设置。

需要注意的事,只有 inline 到 DOM 对象的 CSS 变量能通过这种方式获取,写在 CSS 文件或 style 元素中的 CSS 变量的获取方式尚未找到,有知晓的朋友欢迎留言相告,感谢。

兼容性

目前来看,CSS 变量在 PC 浏览器兼容性比较好,只有 IE 尚未实现。而移动端 IOS safari 已经实现了,安卓需要再等等就可以在生产使用。

当你看到这篇文章的时候可能有所变化,请前往 can i use 查看实时数据。

参考文献


Thanks