点击上方“IT平头哥联盟”,选择“置顶或者星标”
与你一起成长~
公众号回复[ 资源 ] 获取666G前端、Spring Boot 学习资源
作者:安佳
嗨,我叫安佳,是 360 搜索事业部的一名前端开发工程师。去年5月,我有幸加入了 W3C 的 CSS 工作组,成为其中的一员。第一次参与工作组讨论的是关于 [css-grid-2] Allow minmax where max wins over min,很开心自己的提议被标准采纳了,预计会在 CSS Grid Layout Module Level 2 里实现。
期间,看到了一个关于要 CSS 支持原生嵌套的议题 [css-nesting] request to pick up the css-nesting proposal。当时觉得这个特性很好,于是就表达了自己的观点。一周之后,CSS 工作组在周会上对它进行了讨论,但是打的标签依然是 unknown/future spec。又过了两周,看起来并没有要在下一个模块里实现此特性的计划,也没有要商讨的安排。
这么有用的嵌套功能和scope特性,为什么一直坐在冷板凳上呢?于是,我就想探究下原因。这篇文章就是研读此 Issue 及相关规范的成果,主要有这三部分:
- 来自 Web 开发者的呼声:介绍此 Issue 的相关背景
- CSS 工作组都干了什么:介绍工作组的工作内容
- 未来的原生嵌套:介绍嵌套语法
来自 Web 开发者的呼声
2012年4月13日,CodePen 的联合创始人 Chris Coyier 抱怨 CSS 的类名不支持命名空间,导致要写好多重复的选择器。
2016年2月2日,微软的项目经理 Kenneth Auchenberg 说如果 CSS 支持了变量和嵌套,他将不再使用预处理器。
2016年12月8日,《CSS揭秘》的作者 Lea Verou 调研了使用 CSS 预处理器的首要原因(单选题),有 1838 个人参与了投票,最终并列第一的两个理由是嵌套和变量。她觉得是时候该重新考虑 CSS 原生嵌套的问题了。
2017年7月13日,集设计和开发才能于一身的 UI/UX 自由工作者 Sara Soueidan 说嵌套是她最想要的 CSS 功能。
2017年8月15日,node-inspect 的作者 Jan Olaf Krems 说 cssnext 把嵌套定义成了“明天的 CSS”,但他还是想看到原生的 CSS 嵌套,毕竟 JS 的生态系统已经证明避免“每个人都使用自己的半标准语言”绝对是健康的。
2018年2月23日,Lea Verou 再次发声,说她现在还在用 CSS 预处理器写嵌套,一旦 CSS 支持了原生嵌套,她就果断弃用预处理。
2018年5月25日,postcss-preset-env 的作者 Jonathan Neal 再次提议重新考虑下让 CSS 支持原生嵌套(也就是本文章的切入点),这引来了一波热议。
CSS 工作组都干了什么
其实,早在2014年4月3日,W3C 就发布过一个 CSS范围(scoping)模块 的工作草案;2015年9月23日,谷歌的工程师 Tab Atkins 也发布过一个 CSS嵌套模块 的编辑草案。那个时候,CSS 工作组也讨论过嵌套的问题,但并未通过社区的同意(见会议纪要)。
针对 Jonathan Neal 这次的提议,CSS 工作组的讨论流程如下:
图1. CSS工作组的讨论流程
1.要支持原生嵌套
- 嵌套的样式规则是一个普遍的诉求
- 现存的 CSS 预处理器都支持写嵌套,且它是最受欢迎的功能之一
- 有了原生嵌套,就可以不用预处理器了
2.决定仅增加嵌套语法糖
- 开发人员已经习惯了预处理器中的嵌套,嵌套选择器不应该有特殊的优先级
- 局部样式是有用的,但scope能否真正满足开发人员的需求还不明朗
- 嵌套和scope是两个维度的特性,建议先实现已经比较成熟的嵌套
3.嵌套语法,详见下节
最终的结论是,新增 CSS 嵌套模块,默认 ED(Editor Draft,编辑草案) 阶段,由 Tab Atkins 担任编辑,并收集相关 Issues,直到该特性成为 FPWD(First Public Working Draft,首个公开工作草案)。
ED Editor Draft 编辑草案 WD Working Draft 工作草案 CR Candidate Recommendation 候选推荐标准 PR Proposed Recommendation 提议推荐标准 REC Recommendation 推荐标准(最稳定的)
关于标准的诞生过程,可查看 World Wide Web Consortium Process Document
未来的原生嵌套
CSS Nesting Module Level 3 里定义了 CSS 嵌套,它新增了一个新的选择器:嵌套选择器 &
代码语言:javascript复制
a, b { & c { color: blue; }}
/* 等价于 */a c,b c { color: blue; }
看了上面的写法,我想肯定有小伙伴要问了:那个前缀 & 能省略不写吗?
对此,草案里的解释是:现有的 CSS 解析都是通过一个单独的前瞻符(lookahead token)来区分各种选择器的,如果新增的嵌套语法不写前缀的话,那一段文本就没法提前知道它到底是一个 CSS 声明还是一个 CSS 选择器了,这会非常不利于浏览器的实现。
前瞻符,诸如: # ID 选择器 . 类选择器 [] 属性选择器 * 通用选择器 : 伪类 :: 伪元素
图2. CSS 声明
另外,如果省略了 & 也就没法区分 #foo { .bar {} } 到底是复合选择器 #foo.bar 还是组合选择器 #foo .bar 了。
草案里定义了两种嵌套方法:直接嵌套和 @nest 规则
直接嵌套
直接嵌套,即直接以嵌套选择器 & 开头
代码语言:javascript复制
.foo { color: blue; & > .bar { color: red; }}/* 等价于 .foo { color: blue; } .foo > .bar { color: red; } */
.foo { color: blue; &.bar { color: red; }}/* 等价于 .foo { color: blue; } .foo.bar { color: red; } */
.foo, .bar { color: blue; & .baz, &.qux { color: red; }}/* 等价于 .foo, .bar { color: blue; } .foo .baz, .bar .baz, .foo.qux .bar.qux { color: red; } */
/** * 以下写法都是无效的 */.foo { color: red; .bar { color: blue; }}/* 无效原因:没有嵌套选择器 & */
.foo { color: red; .bar & { color:blue; }}/* 无效原因:& 没有在组合选择器的第一位 */
.foo { color: red; &.bar, .baz { color: blue; }}/* 无效原因:列表的第二个选择器里没有嵌套选择器 & */
@nest 规则
代码语言:javascript复制
.foo { color: red; @nest & > .bar { color: blue; }}/* 等价于 .foo { color: red; } .foo > .bar { color: blue; } */
.foo { color: red; @nest .parent & { color: blue; }}/* 等价于 .foo { color: red; } .parent .foo { color: blue; } */
.foo { color: red; @nest :not(&) { color: blue; }}/* 等价于 .foo { color: red; } :not(.foo) { color: blue; } */
/** * 以下写法都是无效的 */.foo { color: red; @nest .bar { color: blue; }}/* 无效原因:没有嵌套选择器 & */
.foo { color: red; @nest & .bar, .baz { color: blue; }}/* 无效原因:列表里并非所有的选择器都包含嵌套选择器 & */
你有想说的吗?
看完本篇文章,你有什么想说的吗?欢迎留言。
当然,你也可以去 CSS 工作组的官方 github 上 w3c/csswg-drafts 提 Issue。若是和嵌套相关的,则 Issue 的标题格式是“[css-nesting]...”;若是和scope相关的,则标题格式是“[css-scoping]...”。诚邀大家提出建设性的意见/建议。
相关规范:
- CSS Nesting Module Level 3
- CSS Scoping Module Level 1
- CSS Cascading and Inheritance Level 3
本文章仅代表个人观点,与 CSS 工作组无关。特此说明
- end -
用心分享 一起成长 做有温度的攻城狮
公众号回复[ 资源 ] 获取666G学习资源