步入 2022,CSS 的新特性层出不穷,而最近在 CSS 圈最受瞩目的新特性,非 CSS @layer 莫属。

本文,将用最简洁的语言,快速让读者们搞懂,到底什么是 CSS @layer 新规范。

过往 CSS 优先级中存在的问题

如果我们的页面上存在非常多的样式,譬如有我们开发页面的时候的自定义样式,也有引入的组件库样式。这时候样式将会非常混乱难以管理

当我们想覆盖一些本身非我们书写的样式时候,往往不得不通过使用优先级权重更高的样式名,去覆盖那些样式。

同时,当样式优先级感到难以控制时,开发者习惯滥用 !important 去解决,这又循环导致了后续更混乱的样式结构。

基于让 CSS 得到更好的控制和管理的背景,CSS @layer 应运而生。

何为 CSS @layer?

CSS @layer 从 CSS Cascading and Inheritance Level 5 被规范定义。

何为 CSS @layer?简单而言,CSS @规则 中的@layer声明了一个 级联层, 同一层内的规则将级联在一起, 这给予了开发者对层叠机制的更多控制。

语法也非常简单,看这样一个例子:

@layer utilities {
/* 创建一个名为 utilities 的级联层 */
}

这样,我们就创建一个名为 utilities 的 @layer 级联层。

@layer 级联层如何使用呢?

通过 @layer 级联层管理样式优先级

@layer 级联层最大的功能,就是用于控制不同样式之间的优先级

看下面这样一个例子,我们定义了两个 @layer 级联层 A 和 B:

<div></div>
div {
width: 200px;
height: 200px;
}
@layer A {
div {
background: blue;
}
}
@layer B {
div {
background: green;
}
}

由于 @layer B 的顺序排在 @layer A 之后,所以 @layer B 内的所有样式优先级都会比 @layer A 高,最终 div 的颜色为 green

当然,如果页面内的 @layer 太多,可能不太好记住所有 @layer 的顺序,因此,还有这样一种写法。

我们可以同时命名多个 @layer 层,其后再补充其中的样式规则。

<div></div>
@layer B, C, A;
div {
width: 200px;
height: 200px;
}
@layer A {
div {
background: blue;
}
}
@layer B {
div {
background: green;
}
}
@layer C {
div {
background: orange;
}
}

上述代码,我们首先定义了 @layer B, C, A 三个 @layer 级联层。而后再后面的 CSS 代码中补充了每个级联层的 CSS 代码,但是样式的优先级为:

A > C > B

因此,最终的 div 的颜色值为 @layer A 中定义的颜色,为 blue

到这里,CSS @layer 的作用可以清晰的被窥见。

利用 CSS @layer,我们可以将 CSS 不同模块划入不同的 @layer 中,利用先后顺序,非常好的去控制全局的样式优先级

@layer 级联层的三种定义引入方式

上面其实提到了两种 @layer 级联层的定义引入方式,这里再描述下,一共有 3 中引入 CSS @layer 级联层的方式。

  1. 直接创建一个块级的 @layer 规则,其中包含作用于该层内部的CSS规则:
@layer utilities {
p {
padding: .5rem;
}
}
  1. 一个级联层可以通过 @import 来创建,规则存在于被引入的样式表内:
@import(utilities.css) layer(utilities);
  1. 创建带命名的级联层,但不指定任何样式。样式随后可在 CSS 内任意位置添加:
@layer utilities;
// ...
// ...
@layer utilities {
p {
color: red;
}
}

非 @layer 包裹层与 @layer 层内样式优先级

当然,这里还会有一种情况,没有被 @layer 包裹的样式,它的优先级和被 @layer 包裹的样式相比,又会如何呢?

看这样一个例子:

@layer A { a { color: red; } }
@layer B { a { color: orange; } }
@layer C { a { color: yellow; } }
a { color: green; } /* 未被 @layer 包裹的样式 */

这里会有一个非常重要的结论,非 @layer 包裹的样式,拥有比 @layer 包裹样式更高的优先级,因此,上述规则的排序是:

未被 @layer 包裹的样式 > @layer C > @layer B > @layer A

匿名层与嵌套层

还有两种层级关系,分别是匿名层嵌套层

匿名层

允许创建一个不带名字的 @layer:

@layer {
p {
margin: 1rem;
}
}

这里,创建了一个匿名层。匿名层的两个重要特性:

  1. 创建后无法向其再添加规则
  2. 该层和其他命名层功能一致,优先级也遵循后定义的匿名层,比其他已定义的 @layer 层,优先级更高

看一个例子:

<div></div>
div {
width: 200px;
height: 200px;
}
@layer {
div {
background: pink;
}
}
@layer B, C, A;
@layer A {
div {
background: blue;
}
}
@layer B {
div {
background: green;
}
}
@layer C {
div {
background: orange;
}
}

上述代码,我们首先定义了一个匿名层,指定了 div 的颜色为 pink,而后又定义了 @layer B, C, A。这里优先级顺序为:

A > C > B > 匿名层

最终的颜色为 @layer A 内的颜色值 -- blue

如果,我们将匿名层放在最后的话:

div {
width: 200px;
height: 200px;
}
@layer B, C, A;
@layer A {
div {
background: blue;
}
}
@layer B {
div {
background: green;
}
}
@layer C {
div {
background: orange;
}
}
@layer {
div {
background: pink;
}
}

此时,样式的优先级顺序为:

匿名层 > A > C > B

最终的颜色为匿名层内的颜色值 -- pink

嵌套层

说完了匿名层,我们再来看看嵌套层。

顾名思义,嵌套层的意思就是在 @layer 内部,我们可以再嵌套使用 @layer 级联层。像是这样:

@layer A {
@layer B{
...
}
}

当然,它还有另外一种语法,上述代码等价于:

@layer A.B {
...
}

了解了这个后,那么,看这样一个例子:

<div></div>
div {
width: 200px;
height: 200px;
}
@layer A {
div {
background: blue;
} @layer B {
div {
background: red;
}
}
}

我们在 @layer A 中嵌套一个 @layer B,同时都定义了一个 div 的样式,最终 div 的 background 到底是什么颜色呢?

最终为蓝色 background: blue,为什么呢?这个很好记忆,我们假设如果没有 @layer A 这一层包裹,其实就是上述说的 @layer 层与非 @layer 层的优先级比较,这里,非 @layer 层(我们可以理解为更高级别的一层 @layer)的优先级更高。

因此,对于单个 @layer 内的嵌套关系,样式优先级为:

@layer A > @layer A.B

多层嵌套层的优先级关系

OK,再看这样一种情况:

div {
width: 200px;
height: 200px;
}
@layer A {
div {
background: blue;
}
@layer B {
div {
background: red;
}
}
}
@layer C {
div {
background: yellow;
}
@layer D {
div {
background: green;
}
}
}

这里存在同时存在多个嵌套 @layer 的情况。那么这种情况优先级又是如何划分呢?

这里的规则是,优先级高的 @layer,无论是否存在嵌套,优先级都整体比优先级低的 @layer(无论是否存在嵌套)高,因此,这里的优先级排序是:

@layer C > @layer C.D > @layer A > @layer A.B

!important 对 CSS @layer 的影响

再来看看 !important 对 CSS @layer 的影响。

这里可以分为几种情况,先看其中一种:

<div></div>
div {
width: 200px;
height: 200px;
background: black;
}
@layer A {
div {
background: blue;
}
@layer B {
div {
background: red;
}
}
}
@layer C {
div {
background: yellow;
}
@layer D {
div {
background: green!important;
}
}
}

上述代码,我们给 @layer C.D 的 <div> 添加了一个 !important 规则。

如果,不考虑 !important 规则,那么实际的 CSS 优先级为(序号越高,优先级越高):

  1. @layer A.B
  2. @layer A
  3. @layer C.D
  4. @layer C
  5. 非 layer 包裹块

那么,<div> 的颜色应该为黑色 black。然而,这里给 @layer C.D 的 <div> 添加了一个 !important 规则。

实际上,最终 <div> 的颜色为 green,也就是最终的优先级排序为(序号越高,优先级越高):

  1. @layer A.B
  2. @layer A
  3. @layer C
  4. 非 layer 包裹块
  5. !important 下的 @layer C.D

也就是说,这里 !important 规则的优先级还是凌驾于非 !important 规则之上的。

上述 DEMO 还是比较有意思的,感兴趣的可以看看:CodePen Demo -- CSS Cascade @layer Demo

非 @layer 包含块 !important 与 @layer 包含块 !important

到这里,你也许会以为你懂了。好,我们再来看一个 DEMO,如果我们给非 @layer 包含块,也加上一个 !important 规则,事情就变得有趣了。

<div></div>
div {
width: 200px;
height: 200px;
background: black!important;
}
@layer A {
div {
background: blue;
}
@layer B {
div {
background: red;
}
}
}
@layer C {
div {
background: yellow;
}
@layer D {
div {
background: green!important;
}
}
}

仔细看上述代码,非 @layer 包含块,我们也加上了一个 !important 规则,按照上述我能描述的规则来看,非 @layer 包含块的优先级高于 @layer 包含块,那么正常而言,我们不难猜测,这里 background: black!important 的优先级应该要比 background: green!important 高,最终 <div> 应该展示黑色。

而实际上,这里最终 <div> 的颜色还是 green。这里就又有一个非常有意思的知识点了,!important 下样式优先级的规则与非 !important 正常状态下刚好相反。

这是一个非常重要的特性,在比较正常(非 !important)规则时,越是级联(排序较后的 @layer 规则),优先级越低;反之,在比较 !important 规则时,越是级联靠后的(排序较后的 @layer 规则),优先级越高。

这个,更进一步的话,我们需要去了解 CSS Cascading 相关的知识了。

CSS Cascade 规范

在 CSS @layer 之前,我们简单看一张图:

上图表面的是在没有 CSS @layer 之前,CSS 样式申明的优先级排序,根据 CSS Cascading 4(Current Work) 标准,定义的当前规范下申明的层叠顺序优先级如下(越往下的优先级越高,下面的规则按升序排列):

  • Normal user agent declarations
  • Normal user declarations
  • Normal author declarations
  • Animation declarations
  • Important author declarations
  • Important user declarations
  • Important user agent declarations
  • Transition declarations

按照上述算法,可以得到一个样式优先级的排序,大概是这样(越往下的优先级越高,下面的规则按升序排列):

  1. User Agent - 用户代理普通样式
  2. User - 用户设置的普通样式
  3. Author - 页面作者普通样式
  4. Animations - 动画样式
  5. ️Author - 页面作者 !important 样式
  6. ️User - 用户设置的 !important 样式
  7. ️User Agent - 用户代理的 !important 样式
  8. Transitions - 过渡样式

简单解释一下

用户代理样式:浏览器会有一个基本的样式表来给任何网页设置默认样式。这些样式统称用户代理样式

页面作者样式:网页的作者可以定义文档的样式,这是最常见的样式表。大多数情况下此类型样式表会定义多个,它们构成网站的视觉和体验,即页面主题,可以理解为页面作者样式

用户样式:读者,作为浏览器的用户,可以使用自定义样式表定制使用体验,自定义用户偏好,可以理解为用户样式

关于 CSS Cascading,也就是层叠规范,你可以看看我的这篇文章加深理解 -- 深入理解 CSS(Cascading Style Sheets)中的层叠(Cascading)

而当有了 CSS @layer 之后,这个层叠优先级顺序有了更新,具体优先级如下:

整体会变更为复杂一些,但是总体还是遵循了两个规则:

  1. !important 样式高于非 !important 样式
  2. 在比较 !important 规则时,优先级顺序与正常规则相反,在正常状态下优先级越低的,在 !important 下优先级越高

总结一下

综上,便是关于 CSS @layer 的基础相关知识。

CSS @layer 的诞生,让我们有能力更好的划分页面的样式层级,更好的处理内部样式与外部引用样式的优先级顺序,属于比较重大的一次革新。

同时,它也让我们意识到要逐渐摒弃大规模使用 !important 去覆盖样式优先级的错误做法,避免许多因为优先级问题带来的不必要的副作用。

当然,时至今天(2022-03-14),我们来看一眼兼容性:

虽然红了一大片,但是,在最新版的 Chrome、Safari、Firefox、Edge 都已经开始支持 CSS @layer,并且,目前已经可以通过一些 polyfill 初步使用它,相信在不久的将来,它将会成为业务 CSS 代码中必不可少的一部分。

延伸阅读

外网对 CSS @layer 已经有非常多的讨论,这里罗列一些优质文章,感兴趣的可以继续阅读:

最后

好了,本文到此结束,希望对你有帮助

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

2022 年最受瞩目的新特性 CSS @layer 到底是个啥?的更多相关文章

  1. TextView 的新特性,Autosizing 到底是如何实现的? | 源码分析

    一.前言 Hi,大家好,我是承香墨影! 前两天聊了一下 Autosizing 的使用,反映还不错.毕竟是这种能解决实际问题的新 Api,确实在需要的时候,用起来会很顺手. 简单回顾一下,Autosiz ...

  2. H5新特性之canvas

    canvas无疑是H5之中最受欢迎的新特性了,它可以让浏览毫无费力的画出各种图案,动画. canvas的性能不会因为画布上的图案多少而改变,因此做动画用canvas性能也相当优秀. canvas最基本 ...

  3. Java9最受期待的5大新特性

    虽然Java9要等到明年才正式发布,但是现在网上已经有了各种各样的有关Java9新特性的文章了,今天小编也将为大家分享除了通常猜测之外的一些很值得期待的5个新特性. 1.Java + REPL = j ...

  4. TypeScript 2.0候选版(RC)已出,哪些新特性值得我们关注?

    注:本文提及到的代码示例下载地址 - Runnable sample to introduce Typescript 2.0 RC new features 作为一个Javascript的超集, Ty ...

  5. Java 8新特性终极指南

    目录结构 介绍 Java语言的新特性 2.1 Lambdas表达式与Functional接口 2.2 接口的默认与静态方法 2.3 方法引用 2.4 重复注解 2.5 更好的类型推测机制 2.6 扩展 ...

  6. UE4.11新特性:胶囊体阴影

    官方介绍 虚幻引擎现在支持非常柔滑的间接阴影,由代表角色的胶囊体来进行投影. 通常,在受间接光照时,并不会产生阴影,除非是屏幕空间环境遮罩.间接投影需要做的非常柔滑,因为间接光照是来自很多不同的方向, ...

  7. ASP.NET 5系列教程 (一):领读新特性

    近期微软发布了ASP.NET 5.0,本次发布的新特性需求源于大量用户的反馈和需求,例如灵活的跨平台运行时和自主部署能力使ASP.NET应用不再受限于IIS.Cloud-ready环境配置降低了云端部 ...

  8. Java8的新特性以及与C#的比较

    函数式接口 VS 委托 在C中,可以使用函数指针来存储函数的入口,从而使得函数可以像变量一样赋值.传递和存储,使得函数的调用变得十分灵活,是实现函数回调的基础.然而函数指针不存在函数的签名信息,甚至可 ...

  9. Oracle 11g新特性

    文章转自网络 Oracle 11g于2007年7月11日美国东部时间11时(北京时间11日22时)正式发布,11g是甲骨文公司30年来发布的最重要的数据库版本,根据用户的需求实现了信息生命周期管理(I ...

随机推荐

  1. mac os 利用ssh 搭建git server服务器详细教程,以及git基本用法

    详细讲mac 连接mac的git操作 首先在服务端上 第一:新建一个仓库 1, cd /Users/userName/projects 用linux命令进入一个你想要创建与他人共享的文件夹. 2,su ...

  2. Ansible 自动化运维——剧本(playbook)

    Ansible 自动化运维--剧本(playbook) 1.playbook介绍: playbook是ansible用于配置,部署,和管理被控节点的剧本.通过playbook的详细描述,执行其中的ta ...

  3. SQL性能优化技巧

    作者:IT王小二 博客:https://itwxe.com 这里就给小伙伴们带来工作中常用的一些 SQL 性能优化技巧总结,包括常见优化十经验.order by 与 group by 优化.分页查询优 ...

  4. 使用Three.js和React把冰墩墩部署在网页上!实现人手一墩!

    前言 最近冰墩墩一墩难求,大家开始通过各种方式打造自己的冰墩墩,各种冰墩墩开始出现,粘土冰墩墩,橘子冰墩墩,3D打印冰墩墩.这次通过前端的方式展示一个3D冰墩墩,现在开始吧. 声明:本文涉及奥运元素3 ...

  5. C# 实例解释面向对象编程中的开闭原则

    在面向对象编程中,SOLID 是五个设计原则的首字母缩写,旨在使软件设计更易于理解.灵活和可维护.这些原则是由美国软件工程师和讲师罗伯特·C·马丁(Robert Cecil Martin)提出的许多原 ...

  6. Note -「Mobius 反演」光速入门

    目录 Preface 数论函数 积性函数 Dirichlet 卷积 Dirichlet 卷积中的特殊函数 Mobius 函数 & Mobius 反演 Mobius 函数 Mobius 反演 基 ...

  7. netty系列之:可以自动通知执行结果的Future,有见过吗?

    目录 简介 JDK异步缘起 netty中的Executor Future的困境和netty的实现 总结 简介 在我的心中,JDK有两个经典版本,第一个就是现在大部分公司都在使用的JDK8,这个版本引入 ...

  8. 这个杀手不太冷-kill家族

    文章目录 kill killall pkill 跑路小技巧 kill家族: kill: # 删除执行中的程序或工作 killall: # 使用进程的名称来杀死进程,使用此指令可以杀死一组同名进程 pk ...

  9. web开发 小方法1-禁止横向滚动

    最近学了学做了个公司的网站  总结了一些小方法 比如取消横向滚动条并禁止的横向滚动 这样就可以吧超出的切掉让网页更和谐 在body 标签 body{ text-align: center; overf ...

  10. Python中类的两种用法

    第一种用法是使用类生成实例对象.类作为实例对象的模版,每个实例创建后,都将拥有类的所有属性和方法. 第二种用法是用类将多个函数(方法)打包封装在一起,让类中的方法相互配合.