特别声明:如果您喜欢小站的内容,可以点击申请会员进行全站阅读。如果您对付费阅读有任何建议或想法,欢迎发送邮件至: airenliao@gmail.com!或添加QQ:874472854(^_^)
很多Web开发者更喜欢将 *CSS自定义属性 称之为 CSS变量。在2020年中社区中有关于CSS自定义属性的讨论到处可见,小站也有很多关于CSS自定义属性的相关教程,而且在很多Web应用中也可以看到CSS自定义属性的身影。虽然如此,但有很多开发者对CSS自定义属性了解的不多,甚至说不怎么理解,也用不好CSS自定义属性。这篇文章和以往介绍CSS自定义属性的文章有所不同,我从不同的角度来阐述CSS自定义属性,主要是希望这篇文章能让Web开发者更好地理解CSS自定义属性,以及如何更好的使用该属性。如果你对该话题感兴趣的话,请继续往下阅读。
CSS自定义属性的发展进程
众所周之,CSS和其他程序语言有一个最大的差异,即 CSS没有变量 这样的概念(也没有逻辑),也正因此,很多Web开发者都觉得CSS是非常简单的,没有技术含量的。
然而,在前端开发者,很多时候希望CSS能像其他程序语言一样,有 变量 的概念,这样利于CSS的编写和维护。比如构建像下图这样的UI Kit:
从上图可以发现,很多UI上都有#0055fe
这个色值,也就是说该色值会在CSS样式表中使用多次:
header {
background-color: #0055fe;
}
label {
color: #0055fe;
}
button {
border: 1px solid #0055fe;
}
想象一下,如果你正在维护一个大型的项目,会涉及很多个组件(或者多个.css
样式文件),即 #0055fe
被分散运用于多个文件中,多个样式块中。突然有一天,你被要求改更颜色(换肤)。那么在没有CSS自定义属性(CSS变量),可能最好的办法就是在整个项目的所有.css
文件中查找#0055fe
,然后再替换。这么做,是多么痛苦的一件事情,而且还容易被遗漏!
这样的模式可以说是痛苦的,但庆幸的是,在CSS社区中开始使用像Sass、LESS和Stylus等CSS处理器,开发者可以在这些处理器中开始使用变量的概念,比如在Sass中:
$primary-color: #0055fe;
header {
background-color: $primary-color;
}
label {
color: $primary-color;
}
button {
border: 1px solid $primary-color;
}
如此一来,我们可以在一个_var.scss
中放置所有样式中会用到的变量,比如$primary-color
,然后在需要的地方引用已定义好的变量,目的是 为了实现CSS的值的可重用性和减少冗余。基于该特性,Web开发者可以轻易的实现换肤效果:
另外,CSS这几年发展和变革是非常地快,而且W3C的CSS工作者也知道,CSS也应该具备“变量”这样的特性,为开发者减少重复性的工作和简化工作,并且减少对CSS处理器工具的依赖。为此,2012年左右,W3C CSS小组为CSS加入了 CSS自定义属性(CSS变量) 模块,并在2017年左右获得大部分主流浏览器的支持。
有了CSS自定义属性后,我们可以像下面这样来维护CSS:
:root {
--primary-color: #0055fe
}
header {
background-color: var(--primary-color);
}
label {
color: var(--primary-color);
}
button {
border: 1px solid var(--primary-color);
}
不过CSS原生的自定义属性(变量),它也有一定的缺陷,比如说无法在声明变量的时候指定其语法类型,比如上面示例中,我们只能在:root{}
中指定--primary-color
自定义属性的值是#0055fe
,它只是个字符串,并不是一个<color>
。
除了原生的CSS中可以声明自定义属性之外,CSS Houdini的 属性和值API 对CSS自定义属性进行了扩展:
对于CSS Houdini中的自定义属性,我更喜欢称之为 CSS Houdini变量。CSS Hounini变量有两种方式来注册,一种是JavaScript来注册:
CSS.registerProperty({
name: '--primary-color',
syntax: '<color>',
inherits: false,
initialValue: '#0055fe'
})
另外一种是使用@property
注册:
@property --primary-color {
syntax: '<color>';
initial-value: #0055fe;
inherits: false;
}
CSS Houdini变量的使用方式和CSS原生的CSS变量使用方式是相同的:
header {
background-color: var(--primary-color);
}
时至今日,你可能还在CSS处理器中使用变量,或许在开始使用原生的CSS变量,也有可能两者混合在一起使用。换句话说,我们有多种方式使用CSS变量,但我们应该根据具体的场景使用更合适合的方式,不过我自己更建议从现在开始就使用原生的CSS变量,因为它有些特性是CSS处理器中变量无法具备的,特别是使用CSS Houdini的变量时,它的特性会变得更强大。在接下来的内容中,有可能会用到CSS Houdini的变量。
CSS自定义属性的基础
上面我们主要介绍了CSS自定义属性(或变量)的发展与变迁,可能对CSS自定义属性了解的还不够深入。如果你从未接触过该方面的特性,可以从CSS自定义属性的一些基础开始,如果你是这方面的专家,你可以选择性的阅读后续的内容。
那我们从CSS自定义属性最基础的开始吧!
CSS自定义属性简述
CSS自定义属性也常被称为CSS变量,被称为CSS变量主要还是源于CSS处理器或其他程序语言的一种叫法。但我想说的是“CSS自定义属性不是变量”。为什么这么说呢?后面会向大家解释。
CSS自定义属性是以--
前缀开始命名,比如前面示例中的--primary-color
,其中primary-color
可以是任何字符串,它也被称为“变量名”。即--变量名
(比如--primary-color
)组合在一起才是“CSS自定义属性”。
CSS自定义属性的声明和Sass的变量声明有所不同,在Sass中,我们可以在非{}
外声明,比如:
$primary-color: #0055fe;
但CSS自定义属性声明需要放置在一个{}
花括号内,比如:
:root {
--primary-color: #0055fe;
}
除了在:root
中之外,还可以是在其他的代码块中,比如:
html {
--primary-color: #0055fe;
}
header {
--primary-color: #00fe55;
}
虽然按上面的方式在CSS中注册了CSS自定义属性,但如果没有被var()
函数引用的话,它们不会有任何效果。比如下面这个示例,只有--primary-color
被var()
引用,而--gap
虽已注册,但未被var()
引用,它也就未运用到任何元素上:
:root {
--primary-color: #0055fe;
--gap: 20px;
}
header {
color: var(--primary-color);
}
除了在CSS中使用--varName
来注册一个CSS自定义属性之外,我们还可以使用JavaScript的style.setProperty()
动态注册一个CSS自定义属性,比如:
document.documentElement.style.setProperty('--primary-color', '#0055fe')
执行完之后,在<html>
元素上会添加style
属性:
<html style="--primary-color: #0055fe"></html>
在CSS Houdini中,我们还可以使用另外两种方式来注册CSS自定义属性(变量)。在CSS样式文件中可以使用@property
注册自定义属性:
@property --primary-color {
syntax: '<color>';
initial-value: #0055fe;
inherits: false;
}
在JavaScript中可以使用CSS.registerProperty()
注册:
CSS.registerProperty({
name: '--primary-color',
syntax: '<color>',
inherits: false,
initialValue: '#0055fe'
})
CSS Houdini中注册好的CSS自定义属性同样只有被var()
函数调用才能生效。
有一点开发者需要特别注意,CSS中注册的自定义属性是有大小写之分的,比如--on
和--ON
是两个不同的CSS自定义属性,比如:
:root {
--ON: 1;
}
.box {
transform: rotate(calc(var(--ON) * 45deg));
transition: transform 1s ease-in-out;
}
.box:hover {
transform: rotate(calc(var(--on) * 720deg));
}
.box:last-of-type:hover{
transform: rotate(calc(var(--ON) * 720deg));
}
如果你把鼠标移动蓝色.box
上,效果和我们预想的并不相同,没有旋转720deg
,反而旋转到了0deg
,即--on
无效值;如果把鼠标移动到红色的.box
上,可以看到元素从45deg
旋转到720deg
:
从浏览器开发者工具中,我们可以得到,var(--on)
(注意,我们在代码中并没有显式声明--on
这个自定义属性),那么transform: rotate(calc(var(--on) * 720deg))
计算出来的transfrom
为none
:
那这就引出了第一个问题:当一个var()
函数使用一个未定义的变量时,会发生什么?
当一个var()
函数使用一个未定义的变量时,会发生什么?
上面的示例告诉我们:var()
函数使用一个未定义的变量(自定义属性)并不会导致样式解析错误,也不会阻止样式加载、解析或渲染。这个就好比你在编写CSS时,因为手误将属性或属性值用错一样,客户端只是不识别这个错误的信息,比如:
那么在使用var()
中使用一些未定义的CSS变量时,有可能是:
var()
函数引用的变量名输错了(手误造成)- 你可能使用
var()
引用了一个自认为它存在的CSS变量,但事实上它并不存在 - 你可能正试图使用一个完全有效的CSS变量,但是你想在其他地方使用,它恰好不可见
我们再来看两个示例,先来看一个有关于border
的示例:
:root {
--primary-color: #0055fe;
}
body {
color: #f36;
}
.box {
border: 5px solid var(--primay-color);
color: var(--primay-color);
}
你将看到的效果如下:
由于手误,在border
和color
的var()
函数事实上引用了一个并未定义的CSS变量--primay-color
(其实是想引用--primary-color
)。结果浏览器并不知道border
最终的值应该是什么?因为border
属性在CSS中是一个不可继承的属性,这个时候浏览器会理解成用户把border
属性值写错了。此时,border
会被浏览器解析为border: medium none currentColor
。
注意,在CSS中,如果
border-style
的值被渲染为none
时,你是看不到任何边框效果的。
再来看color
属性。虽然var()
引用的变量也手误写错了,但它却有颜色。这主要是因为color
是一个可继承的属性,所以浏览器渲染的时候会继承其祖先元素的color
值,在我们这个示例中,在body
中显式设置了color: #f36
,因此.box
的color
继承了body
的color
值,即#f36
。
这个示例令人感到困惑的是var()
引用错语的变量(其实是不存在的变量),浏览器渲染的时候到底会发生什么?从上面的示例中我们可以得知,它的根源在于var()
函数使用了无效的属性,这个时候浏览器渲染CSS时,它自己也无从得知。
浏览器在渲染CSS时,只有属性名或值无法被识别时(浏览器渲染引擎不知道时)才会认为是无效的。但是,var()
函数可以解析为任何东西,所以样式引擎不知道var()
包含的值是否已知(浏览器渲染引擎可识别)。只有当这个属性真正被使用时,它才会知道,这时,它会默默地回退到属性的继承或初始状态,并让你疑惑发生了什么?当你碰到这个现象的时候,其实可以借助浏览器工发者工具来查找问题:
除此之外,在一些浏览器中还提供了自定义属性和其值的查找,有关于这个部分,我们在介绍浏览器开发者工具的时候会向大家演示。
不知道你有没有发现,CSS原生中的自定义属性是一个字符串(前面有提到过),可以说并不很严谨。比如说,--primary-color
应该是一个颜色值(<color>
),但有的时候在另外一个地方再次注册的时候,它可能被开发者定义成一个长度值(<length>
),比如:
:root {
--primary-color: #0055fe;
}
.box {
--primary-color: 5px;
border: solid var(--primary-color);
color: var(--primary-color);
}
效果如下:
可以看到,.box
中的border
引用自己作用域中注册的--primary-color
,浏览器这个时候将其解析为border-width: 5px
,而color
也同时引用了--primary-color
,可相当于color: 5px
,此时浏览器将其继承祖先元素<body>
的color
值。
如果你尝试着将.box{}
中的--primary-color
禁用,此时border
和color
中的--primary-color
将会引用全局的(即:root{}
)中注册的值(--primary-color: #0055fe
)。此时border
中的var(--primary-color)
被浏览器解析为border-color: #0055fe
,而border-width
被解析为medium
。而color
中的var(--primary-color)
也就是一个有效值了。
感觉是不是有点混乱呢?其实这里有一个关于CSS自定义属性(变量)的作用域概念。稍后再探讨。
CSS中的自定义属性的值类型没有任何约束,也就造成上面示例中提到的效果。如果你想对自定义属性值的类型有较强约束的话,就可以使用CSS Houdini的变量了,因为它有syntax
属性来指定自定义属性的值类型。比如:
@property --primary-color {
syntax: '<color>';
initial-value: #0055fe;
inherits: false;
}
这样做除了指定了--primary-color
值类型之外,还可以在var()
直接引用--primary-color
(会解析成初始值,即initial-value
指定的值)。如果你在var()
中引入的--primary-color
自定义属性的值不是<color>
类型,浏览器引用其初始值,比如:
@property --primary-color {
syntax: '<color>';
initial-value: #0055fe;
inherits: false;
}
/* 使用 --primary-color初始值 */
.initial__value {
border: 5px solid var(--primary-color);
}
/* 重置 --primary-color值 */
.new__value {
--primary-color: #09f;
border: 5px solid var(--primary-color);
}
/* 无效值,但会引用--primary-color初始值 */
.invalid__value {
--primary-color: 5px;
border: solid var(--primary-color);
color: var(--primary-color);
}
效果如下:
如需转载,烦请注明出处:https://www.w3cplus.com/css/css-custom-properties-you-know-how-much.html
如果文章中有不对之处,烦请各位大神拍正。如果你觉得这篇文章对你有所帮助,打个赏,让我有更大的动力去创作。(^_^)。看完了?还不过瘾?点击向作者提问!