用 CSS 最困难的部分之一是处理特异性。如果您尝试从像 Bootstrap 这样的框架覆盖样式,这一点尤其明显,但是随着 CSS 层的引入,这一切都发生了变化。这个新功能允许您创建自己的自定义 CSS 层,这是有史以来第一次确定所有 CSS 代码的特异性层次结构。在本文中,我将剖析这对您意味着什么,它是如何工作的,以及您今天如何开始使用它。
创建自己的自定义图层的能力对于 CSS 来说是新功能,但图层从一开始就在 CSS 中存在。CSS 中有 3 个不同的层来管理所有样式的工作方式。
浏览器样式是应用于浏览器的默认样式。这就是为什么 Chrome 和 Safari 中的按钮看起来不同的原因。在浏览器层中找到的样式在浏览器之间是不同的,并且给每个浏览器一个独特的外观。
下一层是用户样式,这并不是您真正需要担心的事情。这些通常是用户可以编写并注入浏览器的自定义样式,但浏览器不再真正支持这些样式。用户可能会更改一些浏览器设置,这些设置会向该图层添加样式,但在大多数情况下,可以完全忽略该图层。
最后,我们来到作者层。这是您最熟悉的层,因为您编写的每一段 CSS 代码都属于这一层
层叠的规则是按照“样式表来源”、“选择器优先级”、“源码顺序”来考虑的,下图展示了一个判定流程:
但是页面一样复杂,组件一多,样式管理起来还是很费劲,很多时候还是得用自己不想用的一些「优先级」权重高的选择器去覆写样式,比如ID或者 !important,所以CSS新出一个 @layer 特性,能让样式得到更好的管理和控制。
大家用过ps的话,很好理解 @layer ,它就像ps里的图层一样,可以对ps图层进行排序,上面的图层有更高的优先级,@layer 的一个重要作用,就是可以提前定义好“级联层”的优先级:
@layer one {
#button.super-specific-selector {
color: red;
}
}
@layer two {
button {
color: green;
}
}
正如你在上面看到的,我们只是使用@layer关键字来创建一个自定义层,给它任何我们想要的名称 。图层two的样式会覆盖图层one的样式。one无论其特殊性如何。
如您所见,我们在作者样式中创建了两个新层,我们可以使用它们来组织我们的代码并使使用特定性更容易。
如何将代码添加到现有层。
@layer one {
#button.super-specific-selector {
color: red;
}
}
@layer two {
button {
color: green;
}
}
@layer one {
.another-style {
color: blue;
}
}
在这个例子中你可以看到我已经定义one了两次图层。这完全没问题,实际上是在创建图层后向图层添加更多样式的方式。这样做不会影响层的顺序,因为层的顺序是由创建层的第一段代码决定的。这意味着我们one在 CSS 文件顶部的第一个图层实例将创建图层,因此本示例中的图层顺序与上一个示例中的相同。唯一的区别是我们能够在图层one创建后通过@layer再次使用关键字来添加额外的样式。等同如下
@layer one {
#button.super-specific-selector {
color: red;
}
.another-style {
color: blue;
}
}
@layer two {
button {
color: green;
}
}
这种在创建图层后向图层添加样式的功能在定义图层顺序时非常有用。想象一下,您有以下几层
@layer base,application;
@layer application {
em {
color: red;
}
}
@layer base {
.item em {
color: green;
}
}
通过@layer base,application;按从左到右的顺序定义所有层,其中列出的base是最不具体的,最后指定的层application是最具体的。也是最先生效的。然后,您可以稍后使用正常@layer语法将代码添加到每个层,而不必担心定义层的顺序,因为它们都在这一行中定义。需要注意的是,这行代码必须在定义任何层之前出现,所以我通常将它作为我的 CSS 文件中的第一行。
很多时候,当你使用一个框架时,你可能会像这样将它导入到你的 CSS 中。
@import url("your.css");
如果要将所有这些导入的代码添加到特定层,只需添加layer(layer-name)到导入语句的末尾即可。
@import url("your.css") layer(layerName);
这会将所有样式添加your.css到layerName图层。但是,使用导入的一件事是它们的性能不是很好,因为首先您需要下载包含该@import语句的样式表,然后浏览器才能下载导入的文件。解决此问题的一种方法是在 HTML 中使用style标签。
<!-- link tag to stylesheet that define your layers -->
<link rel="stylesheet" href="styles.css">
<style>
@import url("your.css") layer(layerName);
</style>
通过像这样编写代码,您可以避免所有性能问题,@import但仍然可以获得直接导入图层的所有好处
匿名层指的是不声明layer名地级联层,它在级联层中的优先级,取决于layer声明次序:
@layer A {
body {
background-color: green;
}
}
@layer {
body {
background-color: red;
}
}
@layer B {
body {
background-color: bisque;
}
}
优先级从高到低为:B > 匿名 > A,所以最后生效的body背景色为bisque。这并不是我觉得太有用的东西,但如果你真的需要将少量的 CSS 代码分成一个层,这可能会很有用。
我认为不太有用的另一个功能是能够将图层相互嵌套
@layer outer {
@layer inner {
.button {
color: red;
}
}
}
@layer outer.inner {
.another {
color: green;
}
}
通过使用上面的.语法或嵌套语法,您可以在其他层内创建层。这是您可能不会经常使用的东西,因为大多数应用程序只有几层,但如果您有一个非常复杂或大型样式系统,这可能会很有用。
这涵盖了创建图层的基础知识,但是您需要了解一些关于图层的概念才能充分利用图层。
到目前为止,我们只处理了所有样式都在图层中的 CSS。当您的样式没有图层时,事情会变得有点复杂。
body {
background-color: blue;
}
@layer A {
body {
background-color: red;
color: black;
}
}
@layer B {
body {
color: blue;
background-color: green;
}
}
在这个例子中,我们有两个个A和B图层,然后是一个没有图层的样式。当您的代码不在任何层中时,它总是被认为比分层代码更具体。这意味着我们的有一个蓝色的背景。为了使这一点更容易理解,我喜欢将不在任何层中的代码视为在所有其他层之后定义的自己的层中。
这有助于我可视化我的代码,因此我可以理解为什么非分层代码总是覆盖层内的代码。
该!important关键字使使用特异性变得困难,并且对于图层来说也没有什么不同。该!important关键字的工作方式与普通图层完全相反。如果您使用!important关键字定义样式,它将覆盖该层之后定义的层中的任何样式。
@layer one {
button {
color: red !important;
}
}
@layer two {
button {
color: green;
}
}
在上面的例子中,按钮文本将是红色的,因为我们之前!important定义了颜色。但是,如果我们尝试添加!important到 layertwo以覆盖!important来自图层one,它实际上不会更改按钮颜色。
@layer one {
button {
color: red !important;
}
}
@layer two {
button {
color: green !important;
}
}
这样做的原因是因为它!important的工作方式与普通图层相反。由于 layerone是在 layer 之前定义的,所以 layer中的two所有!important样式one都会覆盖 layer 中的任何样式,包括!important样式two。这意味着我们的按钮仍然是红色的。
如果我再加一个没有图层!important的普通样式,按钮又是什么颜色的呢?
@layer one {
button {
color: red !important;
}
}
@layer two {
button {
color: green !important;
}
}
button{
color: blue !important;
}
根据非分层样式更先生效,那自然按钮会是蓝色的。
对于每一个很酷的 CSS 功能,你总是需要考虑浏览器的支持,但幸运的是,对我们来说,图层得到了很好的支持,并且很快就会得到完美的支持。目前,layer有84.31% 的支持率,但这主要是因为这个功能最近才在最新版本的浏览器中推出。大多数现代浏览器在撰写本文时不到一个月前就推出了此功能,这意味着我们目前只是在等待用户将他们的浏览器更新到最新版本以支持此功能。
总之,@layer 还是蛮好用的,希望能尽快在业务代码里面用到吧。
介:本文介绍使用Python,爬取国家统计局网站区划和城乡划分代码数据,并保存为不同的格式。该功能可为电商等行业规范客户邮寄地址的填写提供帮助。
行政区划数据取自国家统计局网站(http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/),数据按行政级别由大到下通过链接和纯html文本嵌套呈现。如河北省:
获取顶级区域(省/直辖市/自治区,不含港澳台)的地址是 http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2021/index.html。其中的“2021”代表数据时间版本。
区划代码格式定义参见“民政部行政区划规则(https://www.mca.gov.cn/article/sj/xzqh/1980/)”。
通过Python分析网页,从顶级区域自动识别各级子区域代码和名称,并保存为不同的数据格式,以供其他业务进一步使用。本Python程序可将爬取后的数据另存为
以河北省为例:文件名中的13是河北省区划代码;文件中ID是自定义的数据主键,2021是数据版本号。
html表格文件,文件名 address_model_13.html
Excel格式文件:文件名 address_model_13.xlsx
sql格式文件:文件名 address_model_13.sql
json格式文件:文件名 address_model_13.json。json格式文件是按行政区域的级联形式,不带ID和版本号字段。
Python需要本版3.5以上,且要安装requests,pandas,BeautifulSoup库
程序可在命令行直接运行:
python AddressUtil.py
参数在程序中设置,分别是
province:顶级区域代码,province = 0时获取顶级区域数据,可用于测试,减少获取数据等待的时间
out_format:输出格式
参数取值见源程序
程序运行时会打印获取的区域进度和异常
出现“request timeout”时说明由于网络问题未获取网页需要重试。本程序设置了三次重试,如三次重试仍未获取,则程序退出,请检查你的网络连接和统计局网站是否有问题。
区划代码数据的使用,引用统计局网站原文——“统计用区划代码和城乡划分代码用于统计工作,需要在其他工作中使用时,请务必结合有关实际情况。”
本文涉及的Python程序和数据样例可从百度网盘下载
https://pan.baidu.com/s/1Jhd_4NPn12WtteOW3IP3AA?pwd=1234 提取码: 1234
特别声明:本人保留源程序版权,源程序可供任何人自由使用(包括商业行为),本人不对使用本程序的后果承担任何责任。使用时请标注或保留原作者名:双鱼菜青虫(账号)。
近在Weekly邮件推送中查阅到这样的一条信息:
Chromium 团队宣布他们将随 Chromium 99(预计在明年 3 月发布)一起发布CSS Cascade Layers
会发现这条信息里面出现了一个CSS的新名词CSS Cascade Layers,出于好奇以及对新知识的渴望(说得我自己都信了,哈哈),于是查阅起CSS Cascade Layers的相关资料,试图搞懂它。
前置知识
at-rule规则, CSS Conditional Rules Module Level 3新增的规则,是一条语句,它为CSS提供了执行或如何执行的指令,常见的at-rule规则有:
@import,允许用户从其他样式表导入样式规则
@font-face,允许我们引用自定义的字体
@keyframes,声明一个动画
@media,是条件CSS中的一种,其条件是一个媒体查询
@supports,测试用户代理是否支持CSS属性/值对
@viewport,用来控制移动设备上的viewport设置
级联(层叠)与继承经过多年的发展迭代,目前已有多个版本(CSS2.2、Level3、Level4 和 Level5)
何为级联(层叠)?
层叠本质就是定义了如何合并来自多个源的属性值的算法,简单来说,CSS规则的顺序很重要。当两条同级别的规则应用到一个元素的时候,写在后面的就是实际使用的规则。
h1 {
color: red;
}
h1 {
color: blue;
}
两条规则优先级相同,所以顺序在最后的生效,h1是color:blue'胜出',显示蓝色。
只有CSS声明,就是属性名值对,会参与层叠计算。这表示包含CSS声明以外实体的@规则不参与层叠计算,如包含描述符的@font-face,@规则(at-rule规则)是做为一个整体参与层叠计算。
css属性一般来自于哪几个源?
1、用户代理样式表:浏览器的基本的样式表,用于给所有网页设置默认样式。
2、用户样式表:网页的作者可以定义文档的样式。大多数情况下此类型样式表会定义网站的主题。
3、浏览器的用户使用自定义样式表定制使用体验。
层叠(级联)算法如何过滤来自不同源的css规则?
相互冲突的声明按以下顺序适用,后一种声明将覆盖前一种声明:
1、用户代理样式表中的声明(浏览器的默认样式)。
2、用户样式表中的常规声明(用户设置的自定义样式,就如同我们的reset.css)。
3、作者样式表中的常规声明(开发人员设置的样式)。
4、作者样式表中的!important声明
5、用户样式表中的!important 声明
过滤来自不同源的css规则后,确定同源优先级高低,决定谁“优胜”
!important > 内联style > #id > .class > 标签
了解级联算法有助于帮助我们理解浏览器是如何解决样式规则冲突,也就是浏览器决定哪个样式规则运用到元素上,更多相关css级联的了解:
何为继承?
当元素的一个继承属性没有指定值时,则取父元素的同属性的计算值 。只有文档根元素取该属性定义的默认值,类似的属性有color、font-size等 。
CSS是由Cascading Style Sheets三个词的首字母缩写,很多人将其称为层叠样式表或者级联样式表.
CSS Cascade Layers,也叫做CSS级联层,是Cascading and Inheritance Level5 规范中新增了一个新的 CSS 特性,对应的CSS属性写法@layer,即一个新的 @ 规则,也就是大家所说的at-rule 规则。
为啥会出现@layer?
通过上面我们对级联介绍,我们已经看到了顺序对于层叠的重要性,同权重的css属性后者会“优胜”前者,权重不同会根据CSS声明来源和优先级算法来判断谁“优胜”。!important的CSS规则自动将它跳到层叠算法的前面,能够覆盖普通规则的层叠。
也就是说我们一般会使用选择器权重和顺序作为控制级联的方法,但是这样却会时常碰到:
使用较高权重的选择器来防止你的代码被后面的代码(或别人的代码)覆盖。但这也会引起另一个不良的现象,可能会在代码中新增很多带有 !important 的样式规则,这本身就会引起更多的问题,比如 !important 在 CSS 样式表中随处可见,需要覆盖的时候难以被覆盖 。
使用较低权重的选择器又很容易被后面的代码(或别人的代码)覆盖。比如你在引入第三方代码库或组件时,自己的代码可能被覆盖。
这两个现象也是编写CSS代码,特别是在一个大型项目或多人协作的项目中常出现。也给很多CSS开发者带来很多困扰。
虽然社区有很多第三方方案,如CSS-in-JS、CSS Modules 和 CSS Scoped等来协助解决级联所带来的问题,但由于源码顺序(打包产物)仍然起着决定性的作用,顺序带来的覆盖和冲突依旧未真正的解决,而且选择器权重仍然比层的顺序(源码顺序)更重要。
这样的背景促进了@layer的出现,要真正的解决级联带来的这些问题。
@layer的出现,也要求我们对以往css级联有个新的了解,
可以看出CSS的级联层一般位于“Style 属性”(Style Attribute)和 CSS 选择器权重(Specificity)之间。
使用CSS级联层,可以通过@layer at-rule将 CSS 分成多个层。
与 CSS属性来源 在用户样式表和作者样式表风格之间提供权衡的方式相同,Cascade Layers 提供了一种结构化的方式来组织和权衡单个 来源 内的关注点
如何使用
级联层可以通过多种方式声明:
1、使用@layer 块规则,并立即为其分配样式:
@layer reset {
* { /* Poor Man's Reset */
margin: 0;
padding: 0;
}
}
2、使用规则@layer 语句,没有指定任何样式:
@layer reset;
3、将@import 与layer关键字或layer()函数一起使用
@import(reset.css) layer(reset);
以上每一个都创建了一个名为 的级联层reset。
级联层会按它们声明的顺序排序。
在下面的例子中,我们建立四个级联层:reset,base,theme,和utilities。
@layer reset { /* 创建级联层 “reset” */
* {
margin: 0;
padding: 0;
}
}
@layer base { /* 创建级联层 “base” */
…
}
@layer theme { /* 创建级联层 “theme” */
…
}
@layer utilities { /* 创建级联层 “utilities” */
…
}
按照它们的声明顺序,层顺序变为:
reset
base
theme
utilities
重复使用级联层名称时,样式将附加到现有级联层。级联层的顺序保持不变,因为只有第一次的出现已经确定顺序:
@layer reset { /* 创建第一个级联层 “reset” */
…
}
@layer base { /* 创建第二个级联层 “base” */
…
}
@layer theme { /* 创建第三个级联层 “theme” */
…
}
@layer utilities { /* 创建第四个级联层 “utilities” */
…
}
@layer base { /* 会将样式添加至级联层“base” */
…
}
重新使用级联层名称时层顺序保持不变的使@layer 语法变得更加方便和严谨。使用它,可以预先建立图层顺序,然后将所有 CSS 附加到它:
@layer reset; /* 创建第一个级联层 “reset” */
@layer base; /* 创建第二个级联层 “base” */
@layer theme; /* 创建第三个级联层“theme” */
@layer utilities; /* 创建第四个级联层 “utilities” */
@layer reset { /* 添加样式至级联层 “reset” */
…
}
@layer theme { /* 添加样式至级联层 “theme” */
…
}
@layer base { /* 添加样式至级联层 “base” */
…
}
@layer theme { /* 添加样式至级联层 “theme” */
…
}
当然你可以用更短的语法来声明级联层,
@layer reset, base, theme, utilities;
从上面可以看出,多个级联层被声明时,最后一个级联层的声明会获胜。像这样,
@import(reset.css) layer(reset); /* 第一个级联层 */
@layer base { /* 第二个级联层 */
form input {
font-size: inherit;
}
}
@layer theme { /*第三个级联层 */
input {
font-size: 2rem;
}
}
按以往CSS级联来进行分析的话,form input(多层级)的优先级会大于input,但是由于级联层所起的作用,@layer theme的input会取胜。
级联层支持嵌套使用,如下:
@layer base { /* 第一个级联层*/
p { max-width: 70ch; }
}
@layer framework { /* 第二个级联层 */
@layer base { /* 第二级联层的嵌套子级联层1 */
p { margin-block: 0.75em; }
}
@layer theme { /* 第二级联层的嵌套子级联层2 */
p { color: #222; }
}
}
在这个例子中有两个级联外层:
base
framework
该framework层本身也包含两层:
base
theme
就像一棵树,像这样,
如果要将样式附加到嵌套级联层,需要使用以下全名来引用它,
@layer framework {
@layer default {
p { margin-block: 0.75em; }
}
@layer theme {
p { color: #222; }
}
}
@layer framework.theme {
/* 这些样式会被添加到@layer framework层里面的theme层 */
blockquote { color: rebeccapurple; }
}
@media (min-width: 30em) {
@layer layout {
.title { font-size: x-large; }
}
}
@media (prefers-color-scheme: dark) {
@layer theme {
.title { color: white; }
}
}
如果第一个@media (min-width: 30em)匹配(基于视口尺寸),则layout级联层层将在图层顺序中排在第一位。如果只有@media (prefers-color-scheme: dark)匹配,theme则将是第一层。
如果两者匹配,则图层顺序将为layout, theme。如果没有匹配,则不定义层。
随着 Cascade Layers 的出现,我们的开发人员将拥有更多的工具来控制 Cascade。Cascade Layers 的真正力量来自它在 Cascade 中的独特位置:Style 属性(Style Attribute)和 CSS 选择器权重(Specificity)之间。因此,我们不需要担心其他层中使用的 CSS 的选择器特异性,也不需要担心我们将 CSS 加载到这些层中的顺序.
了解到这里,是不是觉得@layer相当地cool,迫不及待地想去使用了,我们看一下caniuse @layer的兼容情况,
很遗憾,支持程度惨不忍睹,想真正使用可能还要再等等,对于明年三月份 Chromium 99,发布我们拭目以待。
当然现在如果想尝鲜,对于社区也有给出一些办法,
大家也可以试一试,感谢阅读!
*请认真填写需求信息,我们会在24小时内与您取得联系。