作为一名拥有近 15 年经验的前端开发人员,我亲眼目睹了 Web 开发的演变。对我来说,从通过 FTP 上传修改后的文件的时代(是的,GitHub 15 年前就推出了,但我直到 2011 年才发现它)到响应式界面、UI 库和直接生成网站的现代时代已经走过了很长一段路。图玛。 然而,我仍然遇到使用嵌套样式的项目,例如: .some-class ul li div a { /* some style */ } 或者, #nav .nav-link svg { /* some style */ } 这可能看起来令人震惊,但这种编码实践无处不在,从价值数百万美元、快速增长的项目到不起眼的初创公司。 让我们深入研究一下为什么这种方法会带来问题。 嵌套样式的冲突 深度嵌套的样式通常会导致样式冲突,尤其是当您有一个大项目时。 CSS 作为级联样式表,根据元素的特殊性向下级联并应用于元素。由于其特殊性,深度嵌套的样式可能会无意中覆盖其他样式。 考虑这个例子: .some-class ul li div a { color: red; } ... .some-class a { color: blue; } 您可能期望 中的所有链接都是蓝色的。然而,由于第一条规则具有更大的特异性,任何嵌套在 中的链接都将是红色的,而不是蓝色的。这可能会导致意外的视觉不一致并浪费大量调试时间。 .some-class ul > li > div 特异性的复杂性 理解 CSS 中的特殊性(或样式的“权重”)概念对于理解为什么深度嵌套可能很麻烦至关重要。如果多个规则竞争单个元素,特异性决定了应用哪个 CSS 规则。它是根据选择器的类型和数量计算的。 特异性是根据四类加权系统计算的: 内联样式 ID 类、属性和伪类 元素和伪元素 在这里,考虑一下规则: body #content .data img:hover { /* some style */ } 特异性为 。这是一个 ID ( )、两个类( 和 )和两个元素( 和 )。 0 1 2 2 #content .data :hover body img 现在,考虑一下规则: #nav .nav-link svg { /* some style */ } 这里的特异性是 。这是一个 ID ( )、一个类 ( ) 和一个元素 ( )。 0 1 1 1 #nav .nav-link svg 特异性并不像传统的十进制数那样在“结转”系统上运行。例如,特异性为 的选择器不等于特异性为 ,即使在十进制系统中, 和 是等效的。 0 1 0 11 0 1 1 1 11 1+1 最后,通用选择器 ( )、组合器 ( 、 、 、 ' ') 和否定伪类 ( ) 对特异性没有影响。然而,在 参数内,选择器照常计数。 * + > ~ :not() :not() 对于视觉学习者,我推荐 。 这个有关 CSS Specificity 的视频 了解 CSS 特异性及其计算方式可以让您编写更好、更可预测的 CSS,并在样式未按预期应用时调试问题。 规则和特殊性 !important 有时,开发人员在遇到 CSS 特异性冲突的困难时会求助于使用 规则。该规则使 CSS 属性变得非常具体,这意味着它将覆盖几乎所有其他声明。 !important 例如: #nav .nav-link svg { color: blue; } .nav-link svg { color: red !important; } 尽管第一条规则由于 ID 选择器而具有更高的特异性,但由于第二条规则中的 , 的颜色将为红色。 !important svg 过度使用的风险!重要 虽然 可以在解决特异性问题时快速解决,但不建议广泛使用它。过度使用会影响可维护性、可预测性和性能。在较大的项目中,过度使用 通常表明在管理 CSS 特异性方面存在困难。与其诉诸 ,通常更好的做法是投入时间重构 CSS 并减少过于具体的选择器的使用。 !important !important !important 您现在就可以检查您的产品🙂。我检查了我的: 虽然 可能是一个诱人的快速解决方案;这就像用大锤敲开坚果一样。更易于维护的方法是让您的选择器尽可能简单、扁平化,这使得您的 CSS 将来更容易理解、管理和扩展。请记住,赢得一场 战争的最佳方法就是从一开始就不要发动战争。 !important !important 遍历 CSS 树 深度嵌套样式的另一个问题是对浏览器渲染的性能影响。当浏览器将样式应用于元素时,它会从右到左遍历 DOM,从键选择器(在我们的示例中为 和 )开始,并在祖先中移动,直到找到匹配项或到达顶部。样式嵌套越多,遍历所需的时间就越长,可能会影响性能并减慢大型项目中的页面加载时间。 a svg 当您指定 CSS 规则时,例如: .some-class ul li a { /* some style */ } 您可以从树的底部(从 标记)开始可视化此规则,并在树中向上(通过 、 和 )。 a li ul .some-class 浏览器将首先查找 (我的意思是所有) 元素,然后它将检查这些 标签是否在 元素内。之后,它将检查这些 元素是否在 内部,最后,它将检查这些 是否在类为 的元素内部。 所有 a a li li ul ul .some-class 这就是浏览器读取 CSS 选择器的方式,也是复杂选择器会导致页面渲染速度变慢的原因。浏览器必须对每个元素进行多次检查,看看它是否符合指定的规则。规则越深,浏览器必须进行的检查越多,这可能会影响性能。 在大型项目中管理 CSS 的更好实践 CSS 模块 允许您在各个模块中编写 CSS,这些模块的范围仅限于您正在使用的组件的本地范围。这意味着 CSS 模块中的样式仅适用于该特定模块,不会泄漏或影响页面上的其他元素。 CSS 模块 让我们探讨 CSS 模块如何使用散列类名来确保样式封装。当您使用 CSS 模块时,您在 CSS 文件中定义的类名将在编译时进行哈希处理。此哈希创建与您的组件相对应的唯一类名称。让我们看一个例子: 假设您有一个如下定义的 CSS 模块: /* Button.module.css */ .button { color: white; background-color: blue; } 您可以像这样在组件中使用它(我更喜欢将样式对象导入为 而不是 - 这是节省打字时间并提高编码效率的快速提示): s styles import React from 'react'; import s from './Button.module.css'; const Button = () => { return ( <button className={s.button}>Click me</button> ); }; export default Button; 编译应用程序时,呈现的 HTML 可能如下所示: <button class="Button_button__3FQ9Z">Click me</button> 在本例中, 是从 CSS 模块生成的散列类名称。请注意,哈希的确切结构和长度可能会根据您的项目配置而有所不同。 Button_button__3FQ9Z 这个唯一的类名确保您在 中定义的样式仅适用于该按钮,不会影响应用程序中的任何其他元素。它还确保没有其他样式可以影响此按钮,除非它们明确针对散列类名。这种样式封装是 CSS 模块的主要优点之一。 Button.module.css CSS-in-JS 库 处理CSS的另一种流行方法是使用CSS-in-JS库,例如 或 。这些库允许您直接在 JavaScript 中编写 CSS,这有几个好处: styled-components emotion :与 CSS 模块类似,样式的作用域是定义它们的组件。 作用域样式 :根据组件中的 props 或状态创建动态样式很容易。 动态样式 :您可以直接在样式中使用 JavaScript 逻辑,并且与组件相关的所有内容都位于一个位置,这可以使您的代码更易于理解和使用。 改进的开发人员体验 以下是如何在 React 应用程序中使用样式组件的示例: import React from 'react'; import styled from 'styled-components'; const Button = styled.button` color: white; background-color: ${(props) => props.primary ? 'blue' : 'gray'}; `; const App = () => { return ( <div> <Button primary>Primary Button</Button> <Button>Secondary Button</Button> </div> ); }; export default App; 在此示例中, 组件具有根据其 属性而变化的动态样式。 Button primary 边界元方法论 如果您没有使用支持 CSS 模块或类似框架的 JavaScript 框架,您仍然可以使用 BEM(块、元素、修饰符)等命名方法有效地管理 CSS。 BEM 代表“块元素修改器”,它是一种帮助您在 CSS 中创建可重用组件和代码共享的方法。以下是您如何使用 BEM 构建 CSS: /* Block */ .top-menu { } /* Element */ .top-menu__item { } /* Modifier */ .top-menu__item_active { } 在 BEM 中,“块”是本身有意义的独立实体,“元素”是块的一部分,没有独立含义,并且在语义上与其块相关联,而“修饰符”是用于更改外观或行为的块或元素。 使用 BEM 等一致的方法可以使您的 CSS 更易于理解和维护,尤其是在大型项目中。 综上所述 在大型项目中管理 CSS 的方法有多种,从 CSS 模块和 CSS-in-JS 库到 BEM 等命名方法。关键是找到一种适合您的团队和项目的方法并始终如一地应用它。请记住,编写 CSS 不仅涉及编写高效且高性能的代码,还涉及编写易于理解和维护的代码。 快乐编码!