CSS 社区模块化方案
概述简介
在前端快速的发展中, CSS 一直缺乏模块化的概念。由于 CSS 是根据选择器进行全局元素的匹配,它没有作用域可言。但随着项目越来越大,参与的人越来越多,命名冲突的问题日趋严重。于是 CSS 社区逐渐诞生了相应的模块化解决方案:Atomic CSS
、OOCSS
、BEM
、SMACSS
、ITCSS
、CSS Modules
以及 CSS-In-JS
。
模块化分类
- CSS Namespace: 通过人工约定命名规则
- CSS Modules: 一个 CSS 文件,一个独立的模块
- CSS-In-JS: 在 Javascript 中写 CSS
CSS 原生作用域方案
上述中,我们讲解了来自于 CSS 社区的模块化方案,但它们都需要额外的工具或者语法,对于小型项目来说,使用起来还是比较麻烦。但现在有了 CSS 原生作用域的提案,它提供了一种新语法 @scope
来实现局部作用域,或许 CSS 样式隔离的新时代即将到来。 (确切来说,是选择器隔离
)
概念说明
来自 https://drafts.csswg.org/css-cascade-6/#at-ruledef-scope
A scope is a subtree or fragment of a document, which can be used by selectors for more targeted matching. A scope is formed by determining:
- The scoping root node, which acts as the upper bound of the scope, and optionally:
- The scoping limit elements, which act as the lower bounds.
An element is in scope if:
- It is an inclusive descendant of the scoping root, and
- It is not an inclusive descendant of a scoping limit.
作用域可以是 document
的子树或片段,选择器使用它进行更有针对性的匹配。作用域的范围通过如下两点确定:
- 作用域根节点,它作为作用域的上限
- 作用域限制元素,它作为作用域的下限(可选)
如果同时满足以下两种情况,则元素在作用域范围中:
- 它是作用域根节点的 包容性后代
- 它不是作用域限制元素的 包容性后代
语法定义
作用域样式属于 CSS At Rules 中一种,在 CSS 语法中使用 @scope
进行描述,它声明了与一组样式规则关联的作用域根节点和作用域限制元素。 它具有近度优先的特性 (Proximity precedence)
。
css
/* 定义 */
@scope [(<scope-start>)]? [to (<scope-end>)]? {
/* <rule-list> */
}
/* 例 1 */
@scope (.card) {
:scope { /* 匹配 .card 自身样式 */ }
}
/* 例 2 */
@scope (.card) {
> img { /* 匹配 .card > img 样式 */ }
& > img { /* 匹配 .card > img 样式 */ }
:scope > img { /* 匹配 .card > img 样式 */ }
}
/* 例 3 */
@scope (.card) to (.content) {
img { /* 匹配 .card 到 .content 之间 img 样式, 不包含 img.content */ }
}
/* 例 4 */
@scope (.card) to (:scope > .content) {
img { /* 匹配 .card img, 但是不匹配 .card > .content img */ }
}
<scope-start>
是一个 CSS 选择器,用于标识作用域根节点<scope-end>
是一个 CSS 选择器,用于标识作用域限制元素<rule-list>
表示作用域声明的样式
注意事项:
在上面 例子2 中 :scope
和 &
有着相同的作用,都匹配了 .card > img
的样式,但其实两者之间还是有很大的区别:
-
:scope
表示匹配的作用域根节点,而&
则表示匹配作用域根节点的选择器 (可重复使用)。css@scope (.card) { :scope :scope { /* ❌ 不能工作 */ } & > & { /* 匹配 .card > .card 样式 */ } }
-
它们还有一个区别,是在参与 CSS优先级权重计算时,表现不一样,具体会在下个小节 优先级权重 中进行说明。
优先级权重
@scope
本身不会影响 CSS 优先级权重计算,但是 :scope
和 &
对 CSS优先级有着不同的权重
@scope
css
@scope (#card) {
img { /* Specificity = (0,0,1) --- img 为 0,0,1 */ }
}
/* 就优先级权重而言,相当于 :where(#card) img */
:scope
css
@scope (#card) {
:scope img { /* Specificity = (0,1,1) --- :scope 为 0,1,0; img 为 0,0,1 */ }
}
&
css
@scope (#card) {
& img { /* Specificity = (1,0,1) --- & 为 1,0,0; img 为 0,0,1 */ }
}
/* 就优先级权重而言,相当于 :is(#card) img */
应用场景范例
-
@scope (.card) { /* ... */ }
css@scope (.card) { img { border-color: green; } }
-
@scope (.card) to (.card__content) { /* ... */ }
css@scope (.card) to (.card__content) { img { border-color: green; } }
-
@scope { /* ... */ }
html<div class="card"> <div class="card__header"> <style> @scope { img { border-color: green; } } </style> <h1>Card Title</h1> <img src="..." height="32" class="hero"> </div> <div class="card__content"> <p><img src="..." height="32"></p> </div> </div>
浏览器兼容性
目前只有谷歌浏览器 (v118+) 支持了 CSS @scope
特性。不过我相信在不久的将来,主流浏览器会对其有更好的支持度。