3、CSS规范:命名规范(BEM/OOCSS)、选择器深度控制、属性书写顺序、媒体查询管理

CSS 这玩意儿,看着简单,写起来也快。但项目一大了,你就知道什么叫「牵一发而动全身」。我见过太多项目,后期改个按钮颜色,得全局搜索半天,还不敢动,怕把别的地方搞崩了。

说白了,CSS 规范不是为了限制你,是为了救你。今天聊聊我这些年积累下来的一些硬核经验。

3.1 命名规范:BEM 与 OOCSS

先说说命名。我个人习惯用 BEM,也就是 Block、Element、Modifier。为什么?因为它直观,看一眼类名就知道这个元素是干嘛的、属于谁、状态是什么。

BEM 的核心公式:

  • Block:独立的组件,比如 .button.card
  • Element:依赖 Block 的子元素,用双下划线 __,比如 .button__icon
  • Modifier:表示状态或变体,用双连字符 --,比如 .button--primary
/* 不好的写法 */
.btn-red { ... }
.btn-large { ... }

/* 好的 BEM 写法 */
.button { ... }
.button--primary { ... }
.button--large { ... }
.button__icon { ... }
.button__text { ... }

我在项目中遇到过最头疼的事,就是有人把样式写成了「全局污染」。比如写了个 .title,结果页面里所有叫 title 的元素都被影响了。用 BEM 就能完美避开这个问题。

再说 OOCSS(面向对象 CSS)。它的思路是把样式拆成「结构」和「皮肤」两部分。比如一个按钮,它的尺寸、内边距是结构,颜色、阴影是皮肤。这样你就能像搭积木一样组合样式。

我的建议:BEM 和 OOCSS 不冲突,可以混着用。BEM 管命名,OOCSS 管复用。比如 .button--primary 里可以再组合 .shadow--sm 这样的工具类。

3.2 选择器深度控制

你想想看,一个选择器写成 .header .nav .list .item a,这得有多深的嵌套?我见过最夸张的,嵌套了七八层,改样式时得拿尺子量缩进。

为什么会这样?很多时候是因为 CSS 预处理器(如 SCSS)的嵌套功能太方便了,大家无脑嵌套。但这样带来的问题是:

  • 权重过高:后面想覆盖,得写更长的选择器,陷入恶性循环
  • 耦合太强:HTML 结构一变,CSS 就得跟着改
  • 性能损耗:浏览器解析选择器是从右往左的,越深越慢

我曾经接手过一个老项目,一个页面加载要 3 秒,排查下来发现是 CSS 选择器太深,浏览器渲染卡住了。后来我把所有选择器控制在 3 层以内,页面加载直接快了一半。

我给自己定了个规矩:选择器深度不超过 3 层。超过 3 层,要么是组件拆分不合理,要么是命名不规范。

/* 不推荐:嵌套太深 */
.navbar .menu .item .link { ... }

/* 推荐:用 BEM 扁平化 */
.navbar__link { ... }

嗯,这里要注意:不是不能用后代选择器,而是要有节制。能用类名直接定位的,就别绕弯子。

3.3 属性书写顺序

这个点,很多人觉得无所谓。但我告诉你,统一的属性顺序能让你在调试时少花一半时间

我个人习惯按照「布局 → 盒模型 → 排版 → 视觉 → 其他」的顺序来写。为什么?因为布局和盒模型决定了元素的位置和大小,这是最关键的;排版决定了内容怎么展示;视觉是锦上添花。

类别 属性示例
布局 display, position, flex, grid
盒模型 width, height, margin, padding, border
排版 font, line-height, text-align, color
视觉 background, box-shadow, border-radius
其他 cursor, transition, animation
/* 推荐写法 */
.button {
  /* 布局 */
  display: inline-flex;
  align-items: center;
  justify-content: center;

  /* 盒模型 */
  width: 120px;
  height: 40px;
  padding: 8px 16px;
  margin: 4px;

  /* 排版 */
  font-size: 14px;
  font-weight: 500;
  color: #fff;

  /* 视觉 */
  background: #1890ff;
  border-radius: 4px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);

  /* 其他 */
  cursor: pointer;
  transition: all 0.3s;
}

小技巧:可以用 Stylelint 的 order 插件来自动检查属性顺序。团队里统一配置后,代码 review 时就不用再纠结「这个属性该放哪」了。

3.4 媒体查询管理

媒体查询,说白了就是让页面在不同屏幕上都好看。但很多人把它写成了「补丁」,哪里不对补哪里。

我建议把媒体查询集中管理,而不是散落在各个组件里。为什么?因为散落式管理会让你在改断点时,满世界找媒体查询,改一个漏三个。

/* 不推荐:散落式 */
.button {
  font-size: 14px;
}
@media (max-width: 768px) {
  .button {
    font-size: 12px;
  }
}

.card {
  width: 300px;
}
@media (max-width: 768px) {
  .card {
    width: 100%;
  }
}

/* 推荐:集中式 */
/* _responsive.scss */
@media (max-width: 768px) {
  .button {
    font-size: 12px;
  }
  .card {
    width: 100%;
  }
  .header {
    flex-direction: column;
  }
}

我在项目中用过两种方式:

  • 按组件拆分:每个组件文件里,在文件末尾统一写该组件的媒体查询
  • 按断点拆分:单独建一个 _responsive.scss 文件,按断点分块,里面放所有需要适配的组件样式

我个人更倾向第二种。因为当你要调整某个断点时,只需要打开一个文件,所有受影响的组件一目了然。

避坑指南:我曾经在项目里用 min-widthmax-width 混着写,结果某个断点区间出现了样式冲突。后来我统一用 min-width(移动优先),从最小的屏幕往上写,再也没出过问题。

另外,建议把断点值定义成变量,别到处写死数字。

// _variables.scss
$breakpoint-sm: 576px;
$breakpoint-md: 768px;
$breakpoint-lg: 992px;
$breakpoint-xl: 1200px;

// 使用
@media (min-width: $breakpoint-md) {
  .button {
    font-size: 16px;
  }
}

好了,CSS 规范这块,说白了就是「约定大于配置」。团队里统一了命名、选择器深度、属性顺序和媒体查询管理,代码的可维护性至少提升 50%。你想想看,是不是这个理?