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-width 和 max-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%。你想想看,是不是这个理?