在学习基本 CSS 原理时,我们会学习如何编写模块化、可重用和描述性的样式,以确保可维护性。但是,当开发人员参与实际应用程序时,通常感觉不可能添加 UI 功能而不会将样式泄漏到意外区域。 这个问题常常会像滚雪球一样滚入自我实现的循环。理论上仅限于某个元素或类的样式开始出现在它们不属于的地方。这迫使开发人员创建更具体的选择器来覆盖泄漏的样式,然后意外地覆盖全局样式,等等。 严格的类名约定(例如 BEM)是解决此问题的一种理论上的方法。 BEM(块、元素、修饰符)方法是一种命名 CSS 类的系统方法,以确保 CSS 文件内的可重用性和结构。像这样的命名约定可以通过利用领域语言来描述元素及其状态来减少认知负担,并且如果正确实施,可以使大型应用程序的样式更易于维护。 然而,在现实世界中,事情并不总是这样。优先事项可能会发生变化,并且随着变化,实施会变得不一致。对 HTML 结构的微小更改可能需要修改许多 CSS 类名。对于高度交互的前端应用程序,遵循 BEM 模式的类名可能会变得又长又笨拙(例如,app-user-overview__status--is-authenticating),并且不完全遵守命名规则会破坏系统的结构,从而抵消其好处。 考虑到这些挑战,开发人员转向框架也就不足为奇了,Tailwind 是最流行的 CSS 框架。与其试图打一场看似无法获胜的样式之间的特殊性战争,不如放弃 CSS Cascade 并使用保证完全隔离的工具更容易。 开发商更依赖公用事业 我们怎么知道一些开发人员热衷于避免级联样式?这是专门为此目的而设计的“现代”前端工具(例如 CSS-in-JS 框架)的兴起。使用严格限制于特定组件的独立样式看起来像是呼吸新鲜空气。它消除了命名事物的需要——这仍然是最令人讨厌和最耗时的前端任务之一——并且允许开发人员在不完全理解或利用 CSS 继承的好处的情况下提高工作效率。 但放弃 CSS Cascade 也有其自身的问题。例如,在 JavaScript 中编写样式需要大量的构建配置,并且通常会导致样式与组件标记或 HTML 尴尬地混合在一起。我们允许构建工具为我们自动生成选择器和标识符(例如 .jsx-3130221066),而不是仔细考虑命名约定,从而要求开发人员跟上另一种伪语言本身。 (好像理解所有组件的 useEffects 的作用的认知负荷还不够!) 进一步将类命名工作抽象为工具意味着基本调试通常仅限于为开发而编译的特定应用程序版本,而不是利用支持实时调试的本机浏览器功能,例如开发人员工具。 这几乎就像我们需要开发工具来调试我们用来抽象网络已经提供的内容的工具一样——所有这些都是为了摆脱编写标准 CSS 的“痛苦”。 幸运的是,现代 CSS 功能不仅使标准 CSS 的编写更加灵活,而且还为像我们这样的开发人员提供了更多的能力来管理级联并使其为我们工作。 CSS Cascade Layers 是一个很好的例子,但还有另一个功能却令人惊讶地缺乏关注——尽管这种情况正在改变,因为它最近已经与 Baseline 兼容。 CSS @scope At 规则 我认为 CSS @scope at-rule 是解决我们已经讨论过的那种样式泄漏引起的焦虑的潜在疗法,它不会迫使我们为了抽象和额外的构建工具而牺牲原生 Web 优势。 “@scope CSS at-rule 使您能够选择特定 DOM 子树中的元素,精确定位元素,而无需编写难以覆盖的过于特定的选择器,并且不会将选择器与 DOM 结构耦合得太紧密。”— MDN
换句话说,我们可以在特定实例中使用独立的样式,而无需牺牲继承、级联,甚至基本的关注点分离这是前端开发长期存在的指导原则。 另外,它具有出色的浏览器覆盖率。事实上,Firefox 146 在 12 月就添加了对 @scope 的支持,使其首次兼容 Baseline。以下是使用 BEM 模式与 @scope 规则的按钮之间的简单比较: <按钮类=“按钮按钮--primary”> 按钮>
<风格> .button .button__text { /* 按钮文本样式 */ } .button .button__icon { /* 按钮图标样式 */ } .button--primary { 主按钮样式 */ } 风格>
<按钮类=“主按钮”> 点我 <跨度>→跨度> 按钮>
<风格> @scope (.primary-button) { span:first-child { /* 按钮文本样式 */ } span:last-child { /* 按钮图标样式 */ } } 风格>
@scope 规则允许以较低的复杂性实现精确性。开发人员不再需要使用类名创建边界,这反过来又允许他们基于本机 HTML 元素编写选择器,从而消除了对规定 CSS 类名模式的需要。通过简单地消除类名管理的需要,@scope 可以减轻大型项目中与 CSS 相关的恐惧。
基本用法
首先,将 @scope 规则添加到 CSS 中,并插入一个根选择器,样式将被限定在该根选择器中:
@scope (<选择器>) {
/* 样式范围仅限于
因此,例如,如果我们将样式范围限定为
@范围(导航){ a { /* 导航范围内的链接样式 */ }
a:active { /* 活动链接样式 */ }
a:active::before { /* 带有伪元素的活动链接,用于额外的样式 */ }
@media(最大宽度:768px){ a { /* 响应式调整 */ } } }
就其本身而言,这并不是一个突破性的功能。但是,可以将第二个参数添加到范围中以创建下边界,从而有效地定义范围的起点和终点。
/* ul 内的任何 a 元素都不会应用样式 */ @scope (nav) 到 (ul) { 一个{ 字体大小:14px; } }
这种做法称为甜甜圈作用域,可以使用多种方法,包括一系列与 DOM 结构紧密耦合的类似、高度特定的选择器、:not 伪选择器,或者为
/*
结论 实用性优先的 CSS 框架(例如 Tailwind)非常适合原型设计和小型项目。然而,当在涉及多个开发人员的大型项目中使用时,它们的好处很快就会减弱。 近几年来,前端开发变得越来越复杂,CSS 也不例外。虽然 @scope 规则并不是包治百病,但它可以减少对复杂工具的需求。当代替策略类命名或与策略类命名一起使用时,@scope 可以使编写可维护的 CSS 变得更容易、更有趣。 进一步阅读
CSS @scope (MDN) “CSS @scope”,胡安·迭戈·罗德里格斯 (CSS-Tricks) Firefox 146 发行说明 (Firefox) 浏览器支持(CanIUse) 流行的 CSS 框架(CSS 2024 现状) “CSS 中的“C”:Cascade”,Thomas Yip(CSS-Tricks) BEM简介(获取BEM)