GMTC全球大前端技术大会(北京站)门票9折特惠截至本周五,点击立减¥480 了解详情
写点什么

BEM 命名法

2019 年 9 月 26 日

BEM命名法

1 摘要

当你在编写 css 代码的时候,是否遇到这样的困扰: 不知道取什么 class 名? 修改某个组件的样式,担心影响了其他组件? 编写的组件样式如何复用?为了解决这些问题,聪明的程序猿发明了 BEM 命名法。


BEM 命名法,是对 css 命名的一种规范,将页面模块化,隔离样式,提高代码的复用性,减少后期的维护成本。BEM 的意思就是 Block(块)、Element(元素)、modifier(修饰符),通过双下划线__或者双中划–链接。


BEM 通常用于框架开发中,比如微信 WEUI、饿了么 element-ui、有赞 vant 等。笔者也是通过阅读这些优秀框架的源码,学习到了这一套 css 命名大法,从此走上人生巅峰,赢取白富美。


2 为什么需要 BEM 命名法

2.1 样式隔离,避免 css 样式污染

css 样式污染的根本原因,是因为 css 没有作用域。BEM 通过特殊的命名方式,给 css 创造一个“作用域”,就能有效避免 css 样式全局污染。例如,给输入框命名


1# 普通2.base input {}34# BEM命名法5.base-input__inner {}
复制代码


普通的命名法, 会作用于所有 class='base’的后代元素。本来你只想给当前元素加样式,结果不小心影响了其他元素,这就是样式污染。


BEM 命名法,只会作用于 class='base-input__inner’的元素, 达到样式隔离。 不会影响其他元素。


有人会说 css-module, 也能实现 css“作用域”,而且作用域更唯一,我为什么要用 BEM ?


2.2 代码更易覆盖

假如,你正在开发一个通用的输入框组件,用了 css-module。打包后是这个样子


1.base-input-sdFh3sxLwo5uer {}
复制代码


另一个人用了你的输入框组件,但是觉得样式不好看,想修改样式。试了半天,发现,根本无法用 css 精确选择你的组件,因为.base-input 后面的 hash 值是动态的。于是,他捶胸顿足,发誓再也不用你的组件了。


2.3 代码更易读

还是刚刚的输入框组件,base-input__nner,不需要我解释,你一眼就能看出:base 代表基础组件,input 代表输入框组件, inner 是组件中的某一块。


3 什么是 BEM 命名法

BEM 其实是块(block)、元素(element)、修饰符(modifier)的缩写,利用不同的区块,功能以及样式来给元素命名。这三个部分使用__与–连接(这里用两个而不是一个是为了留下用于块儿的命名)。命名约定的模式如下:


1.block{}2.block__element{}3.block--modifier{}
复制代码


block 代表更高级别的抽象或组件


block__element 代表 block 的后代,用于形成一个完整的 block 的整体


block–modifier 代表 block 的不同状态或不同版本


3.1 常用规范

1)block element modifier 包含多个单词时,用一个中划线-链接,如:


el-dropdown-menu el-button


2)block 和 element 用双下划线__链接,例:


表单项 form__item


导航项 menu__item


3)element 和 modifier 用双中划线–链接,如表示按钮的不同状态:


默认:el-button–default


成功:el-button–success


4)用 js 控制样式时,css 命名用 is-开头,如 is-success、is-failed、is-disabled


3.2 常用的元素名

表单元素 form form-item input select radio checkbox switch rate datePicker


导航元素 nav subnav menu tab


提示 alert message messageBox notification


数据展示 table process tree pagiantion


其他 button icon


4 如何用好 BEM 命名法

4.1 页面命名

用 page-开头,page 表示这是一个页面, 而不是组件。


给页面命名时,BEM 可以搭配 css-module 一起使用。既能保证打包后选择器的唯一,又容易调试。例如


1# 编译前2.page-index {}3.page-zufang {}45# 编译后6.page-index-70yGFBg1eKjbSIwN {}7.page-zufang-mFTy62A1t83zjDbh {}
复制代码


使用 css-module,打包后的 clss 名是可以修改的参考


 1# 让打包后的文件更容易识别 2{ 3  test: /\.css$/, 4  use: [ 5    { 6      loader: 'css-loader', 7      options: { 8        modules: true, 9        localIdentName: '[local]--[hash:base64:5]'10      }11    }12  ]13}
复制代码


页面中的选择器,都嵌套在页面根选择器内(.page-xxx),保证所有样式,只作用于当前页面。例如


 1<!- 页面命名 page-home -> 2<div class="page-home"> 3    <div class="the-form"> 4        <div class="the-form-item"> 5            <div class="the-input"></div> 6        </div> 7    </div> 8    <div class="the-table"> 9        <div class="the-table-content"></div>10    </div>11</div>1213.page-home {14    .the-form {}15    .the-form-item {}16    .the-input {}1718    .the-table {}19    .the-table-content {}20}
复制代码


4.2 公共组件命名

用 base-开头,base 表示公共组件。


1<div class="base-input">2    <input class="base-input__inner"/>   3</div>45# 选择器避免嵌套,降低选择器权重6.base-input {}7.base-input__inner {}
复制代码


公共组件的每一个 class 名,带上组件的作用域前缀,如 base-input__inner 的作用域前缀是 base-input。


选择器不宜嵌套,让选择器的权重尽可能低。原因如下:


base-input__inner 已经具有有作用域了,无需再嵌套。


由于组件选择器权重较低,在组件外修改组件样式时,覆盖样式非常方便。


4.3 局部组件命名

用 the-开头,the 表示某一特定的组件。


1<div class="the-header">2    <div class="the-header__title" />3    <div class="the-header__desc">4</div>56# 选择器避免嵌套,降低选择器权重7.the-header {}8.the-header__title {}9.the-header__desc {}
复制代码


局部组件的每一个 class 名,带上组件的作用域前缀,如 the-header__title 的作用域前缀是 the-header。


局部组件,也不宜嵌套, 降低选择器权重。


局部组件也可以搭配 css-module 一起用,因为局部组件只给少数特定页面使用,修改样式,可以在组件内部直接修改。


5 其他注意事项

5.1 命名语义化

怎样衡量你的命名是语义化的?让一个人新人,来看一下你的代码,不需要解释,就能知道这个类的作用,就比较语义化。


通常,可以根据模块的功能而命名,如页面头部 header、导航栏 nav、主体 main、侧边栏 sidebar、底部 footer 等,这样整个页面看起来就比较清晰了,维护起来也比较方便。


1# bad2.fl { ... }3.fr { ... }45# good 6# 左浮动7.is-float-left { ... }8# 右浮动9.is-float-right { ... }
复制代码


上面的代码,fl、fr 之类的命名,表达意思不够清晰,要知道具体的含义,还得去看代码。


而 is-float-left,,就表达得非常清晰。


5.2 使用 class(类) 选择器,避免使用 id、标签、伪类选择器

标签、伪类 等选择器范围太广,不具有“作用域”的作用,会污染全局样式。例如,下面的代码中,.the-header a 选择器会选中 the-header 所有后代元素


1<div class="the-header">2    <a></a>3</div>4# bad5.the-header a { ... }
复制代码


5.3 覆盖第三方组件样式时,重新起一个 class 名

使用原来的 class 名修改样式,可能会不小心影响了后代组件的样式。为了消除这个隐患,可以重新起一个 class 名,只作用于当前组件。例如修改 ant-design 的输入框组件样式


1<div className="the-form">2    <Input className="the-input">3</div>45# bad 会影响the-form 后代的所有输入框6.the-form .ant-input {}78# good 只会只用于className='the-input'的输入框9.the-form .the-input{}
复制代码


6 总结

  • 为什么需要 BEM 命名法

  • 什么是 BEM 命名法

  • 如何用好 BEM 命名法

  • 其他注意事项


作者介绍:


包龙星(企业代号名),目前负责贝壳平台技术中心河图的前端研发工作。


本文转载自公众号贝壳产品技术(ID:gh_9afeb423f390)。


原文链接:


https://mp.weixin.qq.com/s/BWWuI_O73kGz0qzYxZzrOg


2019 年 9 月 26 日 10:20770

评论

发布
暂无评论
发现更多内容

GameStop: 究竟发生了什么?

lidaobing

28天写作 逼空

架构师训练营第10周课后作业

万有引力

小产品、连接和生态

孙苏勇

产品 产品定位

创业失败启示录|茶之玄学

青城

28天写作 创业失败启示录 青城

如何做到超预期供应

熊斌

28天写作

技术创业,股权设置的常见“坑” | 视频号28天(24)

赵新龙

28天写作

Elasticsearch document routing 数据路由

escray

elastic 七日更 28天写作 死磕Elasticsearch 60天通过Elastic认证考试

我看好数据湖的未来,但不看好数据湖的现在

王知无

大数据 数据湖

外企时代已经过去?

李忠良

28天写作

同城快递架构设计

Mars

Mybatis【17】-- Mybatis自关联查询一对多查询

秦怀杂货店

数据库 mybatis

数据结构和算法学习总结-复杂度分析

Nick

时间复杂度 数据结构与算法 复杂度

读2020年Javascript趋势报告展望ES2020

devpoint

ES2020 构建工具 前端构建

2021年,开发者的落日

王知无

大数据

怀着期待开启美好的一天「幻想短篇 23/28」

道伟

28天写作

【Vue2】文本无缝滚动

学习委员

Vue 前端 28天写作

管事情的过程,如何聚焦并决策高价值的事?

一笑

管理 优先级队列 28天写作

week10-总结

J

碎碎念之「创造力可能是新的生产力」

Justin

创意 28天写作 创造性思维

week10-homework

J

Reactive Spring实战 -- 响应式Redis交互

binecy

redis Reactive Spring

我的电脑嘲讽我!

IT蜗壳-Tango

七日更

年关将至

luojiahu

《学会写作》学习笔记之如何选题

JiangX

28天写作

【计算机内功修炼】八:函数运行时在内存中是什么样子?

码农的荒岛求生

高并发 内存 高性能 内存管理 运行时栈帧

VS2019 + Qt Creator 4.11.1 导入Qt源码进行调试记录

Creep

c++ qt

Spring 动态代理时是如何解决循环依赖的?为什么要使用三级缓存?

程序员小航

spring 源码

项目管理知识标准体系

Ian哥

28天写作

发布 Go Modules

Rayjun

go go modules

聊聊我的原创维权二三事

架构精进之路

自我思考 七日更 28天写作

机器学习笔记之:虽然还没明白到底是在干嘛,但竟然还觉得挺有意思的!

Nydia

BEM命名法-InfoQ