登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  前端

CSS nesting 怎样组织组件内嵌套规则

来源:17golang原创

时间:2026-10-21 19:19:44 227浏览 收藏

CSS nesting 适合把一个组件的基础样式、子元素样式和状态样式放在同一个规则块里。浏览器会直接解析嵌套规则:普通嵌套选择器默认与父选择器形成后代关系,写出 & 时则可以明确要求父选择器参与拼接,例如把 &:hover 展开成 .card:hover。

它不是必须经过 Sass、Less 之类预处理器的语法。本文只讨论原生 CSS 的选择器展开、组合器、条件规则和特异性,不把嵌套当成全新的组件隔离机制。

官方资料:https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Nesting

规范入口:https://drafts.csswg.org/css-nesting/

先看一个能直接落地的组件写法

.card {
  /* 只放卡片自身的布局与外观 */
  padding: 1rem;
  border: 1px solid #d8dee9;
  border-radius: 0.75rem;

  .title {
    /* 默认与父选择器形成后代关系:.card .title */
    margin: 0;
    color: #203047;
  }

  &:hover {
    /* & 直接代表父选择器:.card:hover,而不是 .card :hover */
    border-color: #2f7cf6;
  }
}

这段 CSS 的关键不是减少了几个字符,而是让相关规则共享同一个组件边界。.title 前面没有 &,所以默认得到 .card .title;&:hover 明确表示伪类要紧贴父选择器,得到的是 .card:hover。

CSS nesting 从父选择器到嵌套选择器和展开结果的静态关系
图1:组件规则、父选择器和展开结果的静态关系;这是说明图,不是浏览器截图或运行证据。

默认后代关系和 & 不是一回事

原生 nesting 允许把样式规则放进另一条样式规则中。嵌套选择器默认相对父选择器解释,相当于在两者之间加入后代组合器。需要精确控制关系时,要把组合器或 & 写出来。

.menu {
  /* 没有 &:等价于 .menu .item */
  .item {
    color: #40536d;
  }

  /* >:只匹配直接子元素 */
  > .item {
    padding-inline: 0.75rem;
  }

  /* & +:父选择器与下一个同级元素相邻 */
  & + .menu {
    margin-block-start: 1rem;
  }
}

这里的三个关系分别是 .menu .item、.menu > .item 和 .menu + .menu。如果写成 & .item,仍然是显式的后代关系;如果把 & 省略,浏览器也会按默认后代关系处理。

最常见的误读是把 :hover 当成可以直接嵌套的后缀。.card { :hover { ... } } 表达的是卡片内部某个后代元素悬停,而不是卡片本身悬停;卡片自身状态应写成 &:hover。

把媒体查询放在组件内部,但别混淆作用域

.card {
  /* 基础规则先保持可读,窄屏默认使用单列 */
  display: grid;
  gap: 0.75rem;

  @media (width >= 48rem) {
    /* 媒体查询仍位于 .card 的嵌套上下文中 */
    grid-template-columns: 12rem 1fr;

    .title {
      /* 这里仍然指向 .card .title */
      align-self: center;
    }
  }
}

媒体查询可以嵌套在组件规则内部,读者能在同一处看到组件的响应式变化。它改变的是条件,不会把 .title 变成全局选择器;理解这点后,可以把组件的基础规则和宽度条件放在一起维护。

如果项目仍需兼容不支持 nesting 的处理链,可保留一份扁平选择器作为回滚路径,或者由构建链统一转换。不要在同一文件里无意识地混用两套写法,否则后续排查时很难判断到底是浏览器解析、构建转换还是层叠顺序造成差异。

样式不生效时,先查 & 和特异性

& 的特异性不是简单地按一个普通字符计算。关联选择器列表中,特异性最高的选择器会影响嵌套结果;因此父规则里如果混入高特异性的 ID 选择器,嵌套规则可能比预想更难覆盖。

#shell, .card {
  /* 关联列表中包含 ID,会抬高嵌套 & 的特异性 */
  & .title {
    color: #153e75;
  }
}

.card .title {
  /* 仅增加 class 可能仍然覆盖不了上面的嵌套规则 */
  color: #64748b;
}

排查时先把嵌套规则展开成普通选择器,再比较选择器、层叠层和出现顺序。若只是希望状态样式覆盖基础样式,优先使用一致的 class 结构和明确的 &,不要靠不断增加选择器长度解决问题。

CSS nesting 中默认后代、显式父选择器、组合器和特异性边界的静态关系
图2:相对选择器、显式组合关系和特异性边界的静态说明;图中关系用于阅读代码,不代表执行顺序。

需要回滚时,用扁平规则保留同一语义

回滚的目标是保留选择器语义,而不是简单删除缩进。可以把嵌套块拆成普通规则,逐条对照默认后代关系与显式 & 的展开结果。

/* 原来的嵌套写法 */
.card {
  /* 回滚时保留基础规则 */
  padding: 1rem;

  &:hover {
    /* 显式父选择器展开为 .card:hover */
    border-color: #2f7cf6;
  }
}

/* 扁平回滚写法 */
.card:hover {
  /* 只在这里保留状态规则,避免误写成 .card :hover */
  border-color: #2f7cf6;
}

如果要判断当前处理链是否接受 nesting,可以把能力检测放在构建或兼容策略中;不要把“能够写进源文件”误认为“所有目标浏览器和构建链都已经按同一规则处理”。文章主题只需要确认选择器语义,实际项目还应按自己的目标环境决定是否保留降级方案。

提交前按这份清单整理组件样式

  • 先确认嵌套规则对应的是组件边界,而不是为了缩短文件盲目加深层级。
  • 普通子选择器按默认后代关系理解;组件自身的伪类、伪元素优先写成 &:hover、&::before。
  • 子代、相邻兄弟或通用兄弟关系直接写出 >、+、~,别靠缩进猜测。
  • 把媒体查询、支持查询等条件规则放在关联组件附近,同时保留清晰的基础样式。
  • 样式覆盖异常时,先展开选择器比较特异性,再检查层叠层和规则顺序。
  • 需要兼容旧处理链时,用扁平选择器保存回滚版本,避免把两套语义写成不同规则。

CSS nesting 的价值是让“同一个组件的规则”在源文件中保持邻近。只要记住默认后代关系、& 的显式父选择器语义,以及展开后的特异性,组件内嵌套就能保持可读而不变成选择器谜题。

两个容易混淆的问题

嵌套规则是不是只能写在 Sass 里? 不是。CSS nesting 是浏览器 CSS 语法的一部分,但目标项目是否直接交给浏览器解析,仍取决于目标环境和构建链。

为什么写了 :hover 却没有匹配卡片? 在父规则里直接写 :hover 通常表示父元素内部的后代伪类位置;卡片自身状态应显式写成 &:hover,让展开结果成为 .card:hover。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>