@layer
规则
接下来介绍非常实用的
@layer
规则。
@layer
规则解决的问题
先讲解一下设计
@layer
的初衷。
我们在实际开发的时候,经常会使用第三方组件。每个产品通常都有自己的UI样式风格,当引入第三方组件的时候,往往需要对这些组件的UI进行重置,例如换肤、变色等。此时,我们的做法是使用优先级更高的选择器进行覆盖,例如第三方组件中某个按钮的选择器是:
.container .some-button { height: 30px; }
当需要重置的时候,可能就会使用类似于下面的选择器,通过增加选择器复杂度的方式进行重置。
.reset .container .some-button { height: 40px; }
这就会使我们的CSS代码变得很臃肿,维护成本上升,同时过于复杂的选择器也使CSS渲染的性能不是很好。
又如这个困扰我很久的例子,对于链接元素的CSS
reset
,有一种非常好的方法是使用
:any-link
伪类,代码示意如下:
:any-link { color: darkblue; }
:any-link:hover { color: blue; }
其语义明确,匹配精准,且无须担心
:visited
伪类的干扰,可以参见10.2节讲解的超链接伪类。
然而,这个伪类却很少有人使用,其原因只有一个,那就是伪类的优先级比较高,不太适合作为CSS
reset
使用,因为一旦设置这个伪类,某个
<a>
元素按钮想要被重置颜色,所需的选择器成本就会很高,提升了整个项目的选择器复杂度。
而有了
@layer
规则,上面这些问题就迎刃而解了。我们只要将希望获得低优先级的CSS代码放在
@layer
规则中,就无须再担心选择器优先级过高的问题,因为
@layer
规则的级联层级比常规的CSS代码的级联层级低。
参见这里的CSS代码示意:
@layer {
.container .some-button { height: 30px; }
:any-link { color: darkblue; }
:any-link:hover { color: blue; }
}
/* 业务代码 */
.some-button { height: 40px; }
a { color: deepskyblue; }
此时相关的CSS代码在浏览器的优先级层级关系如图2-5和图2-6所示。
图2-5 按钮的单类名优先级更高
图2-6
<a>
元素优先级高于
:any-link
伪类
从图2-5可以看出,虽然业务代码中的按钮选择器只有一个类名
.some-button
,其优先级低于
.container
和
.some-button
这两个类名,但是由于代码所在的级联层级更高,因此,还是重置了
30px
。
从图2-6可以看出,链接的颜色最终按照
a
标签选择器渲染了,再也不用担心
:any-link
伪类作为CSS
reset
代码会影响业务代码中
<a>
元素的样式设置了。
眼见为实,读者可以输入https://demo.cssworld.cn/selector2/2/2-1.php或者扫描下面的二维码查看对应的效果。
这就是
@layer
规则的作用,可以让CSS代码的级联层级降低,从而确保主业务的CSS代码不受第三方组件的CSS代码的影响。
@layer
规则的语法
@layer
这个AT规则(CSS at-rule)的语法如下:
@layer {rules}
@layer layer-name {rules};
@layer layer-name;
@layer layer-name, layer-name, layer-name;
其中,
@layer {rules}
语法在前文出现过,没有任何层级名,称为匿名级联层,而下面3种语法均需要自定义级联层的名称,称为命名级联层。
下面重点介绍这3种命名级联层语法。
这种语法和匿名级联层语法的唯一区别就是多了一个名称,便于开发人员识别与管理,从性质上来讲,有点类似于编程语言中的变量。
例如:
@layer button {
.container .some-button {
height: 30px;
}
}
@layer link {
:any-link {
color: blue;
}
}
此时,我们可以使用下面的单命名语法或者多命名语法来灵活调整不同级联层的优先级顺序。如果我们没有这样的需求,则可以直接使用匿名级联层语法。
@layer layer-name
主要用于灵活设置
@layer
规则之间的优先级顺序。例如,有一个级联层,名为
peacock
,希望这个级联层的优先级最低,但是,相关CSS代码的位置却无法控制,有可能靠前,也可能靠后,此时,
@layer layer-name
这个语法就有用武之地了。
@layer peacock;
/* ……大量的CSS代码…… */
/* ……大量的CSS代码…… */
/* ……大量的CSS代码…… */
/* 虽然我位置靠后,但我优先级最低 */
@layer peacock {
.bottom-layer {
content: 'hello world'
}
}
上面这段CSS代码,虽然
@layer peacock{}
出现在CSS语句的最后面,但是由于在开头设置了
@layer peacock;
这行代码,
peacock
这个级联层中的所有CSS代码的优先级都是最低的。
@layer layer-name, layer-name, layer-name
这个多命名语法和
@layer
layer-name
这个单命名语法的作用是类似的,也是用来灵活调整
@layer
规则的整体优先级的。
在默认情况下,
@layer
规则内CSS声明的优先级取决于先后顺序,例如:
@layer b1 {
button { padding: 10px; }
}
@layer b2 {
button { padding: 20px; }
}
此时,如果页面中有一个
<button>
按钮元素,则此按钮元素的内间距是
20px
,因为设置
padding:20px
的规则出现在后面。
如果我们希望
b2
级联层的优先级高于
b1
级联层的优先级,则使用多命名语法设置好先后顺序就可以了。
@layer b2, b1;
@layer b1 {
button { padding: 10px; }
}
@layer b2 {
button { padding: 20px; }
}
此时,按钮元素匹配的
padding
内间距值是
10px
,因为
@layer
多命名语法中
b1
出现在后面,优先级更高,参见图2-7所示的优先级覆盖效果。
图2-7 多命名级联层语法的作用示意
眼见为实,读者可以输入https://demo.cssworld.cn/selector2/2/2-2.php或者扫描下面的二维码查看对应的效果。
@layer
对于第三方的CSS文件,尤其是那些通过CDN实现的绝对地址CSS,我们是没办法修改相关的代码的,此时有办法使这部分CSS变成低优先级的级联层吗?答案是可以的,我们可以尝试使用
@import
语法。
如果希望导入其他CSS文件的低优先级,可以这样设置:
@import './example.css' layer(example);
也就是在传统的
@import
语法后面再添加一个
layer(some-name)
就可以了。
此时,
example.css
中的所有CSS声明的优先级都低于常规设置的CSS声明。其中
layer()
函数中的名称可以自行定义,如果想要调整
layer(some-name)
的优先级,可以参照多命名语法的用法。例如:
layer button, example;
@import './example.css' layer(example);
@layer button {}
同时也支持匿名引入的语法,例如:
@import './example.css' layer;
@layer
规则的嵌套
@layer
规则还支持更加复杂的嵌套语法。先看一个比较简单的嵌套例子:
@layer outer {
button {
width: 100px;
height: 30px;
}
@layer inner {
button {
height: 40px;
width: 160px;
}
}
}
此时,
button
选择器的外层优先级高于内层。读者可以这么理解:每多一层
@layer
,样式的优先级就降低一层。因此,上面的代码中最终生效的是外层的
width:100px
和
height:30px
,效果如图2-8所示。
图2-8
@layer
规则嵌套语法的优先级渲染效果
此时在开发者工具的样式面板中可以看到图2-9所示的CSS代码优先级覆盖关系。
图2-9
@layer
规则嵌套语法的优先级覆盖示意
眼见为实,读者可以输入https://demo.cssworld.cn/selector2/2/2-3.php或者扫描下面的二维码查看对应的效果。
.
)级联写法
内外嵌套的语法还可以使用字符点(.)进行连接,例如,上面例子中的CSS代码和下面的CSS代码的效果是完全一样的,参见图2-8。
@layer outer {
button {
width: 100px;
height: 30px;
}
}
@layer outer.inner {
button {
height: 40px;
width: 160px;
}
}
嵌套的层数不限,例如嵌套5层、10层甚至更多层都是可以的,当然,实际开发中不会用到这样深的层级关系。
当存在多个
@layer
规则,同时这些
@layer
规则之间都有嵌套关系的时候,各个CSS声明的优先级又是怎样的呢?只需要记住这样一句话:内层
@layer
规则的优先级由外层
@layer
规则决定。例如下面这个例子:
@layer 甲 {
p { color: red; }
@layer 乙 {
p { color: green; }
}
}
@layer 丙 {
p { color: orange; }
@layer 丁 {
p { color: blue; }
}
}
由于“丙”位置靠后,因此“丙”的优先级高于“甲”,而对于单独某个级联层的优先级,则是外层的优先级更高,因此,最终的优先级顺序是:丙 > 丙.丁 > 甲 > 甲.乙
真实渲染的覆盖关系如图2-10所示:
图2-10
@layer
规则在多嵌套语法下的优先级覆盖示意
因此,最终
<p>
元素应用的
color
属性值是
orange
。
眼见为实,读者可以输入https://demo.cssworld.cn/selector2/2/2-3.php或者扫描下面的二维码查看对应的效果。