感谢支持ayqy我的订阅号,每周义务推送1篇(only unique one)原创精品博文,话题包括但不限于前端、Node、Android、数学(WebGL)、语文(课外书读后感)、英语(文档翻译)
若是以为弱水三千,一瓢太少,能够去 http://blog.ayqy.net 看个痛快 css
一.BEM简介
BEM自称是前端开发方法论(Frontend Development Methodology),提供了包括命名规则、CSS/JS模块化原则、构建工具在内的一套用于开发环节的方法前端
P.S.强调开发环节是为了与Yeoman之类的东西区分开,BEM想说的是方法,而不是工具web
提供了一套命名规则(BEM命名规则),主要用于模块化CSS,但BEM中也用在了JS以及文件命名等各个方面编程
CSS中主要解决了这些问题:微信
团队协做中样式命名(好比class)冲突app
用长
class
名解决,不使用结构化选择器(如子选择器、后代选择器等等),类名自带层级关系框架实现代码本身会说话(self-documenting code)的目标模块化
语义化类名,提供更多的信息,例如元素名、功能、所属组件名等等svn
避免组件之间相互影响工具
不依赖结构化选择器,全靠类名
怎样才能在同一DOM节点上组装各类特性,同时避免重复代码(复制粘贴)
Mix,例如
div.classA>div.classB
组合Block A和Block B
这些解决方案在如何写好CSS都有说明,若是只是定义了一套长类名格式的话,确实没什么意义,因此BEM还为这一套命名实现了框架和工具,确保开发过程当中能用好用
二.术语概念
(建议跳过,直接看红色部分,官方解释等于没解释)
Block
逻辑和页面功能都独立的页面组件,是一个可复用单元,特色以下:
能够随意嵌套组合
能够放在任意页面的任意位置,不影响功能和外观
可复用,界面能够有任意多个相同Block的实例
Element
Block的组成部分,依赖Block存在(出了Block就不能用)
Modifier
[可选]定义Block和Element的外观及行为,就像HTML属性同样,能让同一种Block看起来不同
BEM entity
上面三个都是
Mix
单一DOM节点上各个BEM entity构成的一个实例,特色以下:
能把几个BEM entity的行为、样式结合起来,避免代码重复
基于现有BEM entity语义化地建立新的界面组件
BEM tree
用BEM对web页面结构进行描述
Block implementation
不少不一样的技术决定了BEM entity的行为、外观、测试、模板、文档、依赖描述、额外数据(例如图片)等方面
Implementation technology
用来实现一个Block的技术,能够是一种或者多种
Block redefinition
在不一样层面上经过给Block添加新特性来修改Block的实现
Redefinition level
一系列BEM entity及其部分实现
使用中能够看做逻辑层级,例如project level、library level,前者能够重写后者Block的功能及外观
P.S.实际是经过文件目录和按顺序引入实现的层级隔离和重写
须要关注的点:
B(Block):表示模块,最小的可复用单元,功能独立,能够嵌套、组合使用
E(Element):B的组成部分
M(Modifier):表示E的状态(不一样状态下的E有不一样的功能和外观),也是B的组成部分
BEM tree:用BEM术语描述页面/项目结构
Block implementation:实现Block须要的东西,包括全部相关内容,好比JS,CSS,image等等
Redefinition level:能够看做逻辑层级,在高层能够重写/扩展低层Block(模块)
三.BEM命名规则
block-name__element-name_mod-name
,例如nav-menu__item_active
block-name
自己可能并不对应样式,而只做为Block的逻辑名
BEM只是提供通常方法,并无限定必须使用这种命名规则
四.JavaScript for BEM
命名规则一样适用于JS
JS中,Modifier用来表达Block或者Element的逻辑(CSS中,Modifier用来定义外观),JS经过一系列状态来描述Block和Element的行为
不经过class
来查找组件,而是经过Block名(HTML中,不仅经过class
惟一标识,也能够是标签、属性等等),例如:
document.querySelector('.button') .addEventListener('click', function() { document.querySelector('.popup').classList.toggle('popup_visible'); }, false); // BEM Style(伪代码) block('button').click(function() { block('popup').toggleMod('visible'); });
实际应用须要引入i-bem.js
Modifier 能够设置Block的具体状态,经过添加和移除Modifier来实现Block的状态切换,对Modifier的操做会触发事件,通知相关Block。 为了确保这套机制正确运行,不该该容许时随意切换状态或者直接修改某个DOM节点的class,这些操做必须经过BEM提供的helper来实现(这种限 制相似于svn本地仓库)
Modifier对应状态,状态关联功能。添加Modifier时,状态发生变化,自动应用相应功能;移除Modifier时,相应功能会被自动移除
定义Modifier和状态以及底层之间的关联:
block('button').onSetMod({ focused: { true: this.onFocus, '': this.onBlur } });
屏蔽了 状态之下的东西(多是添加/移除Modifier引发状态改变,或者是用户行为触发的),在编程层面须要关注的最小单元是状态(Modifier)。可 以经过给Modifier添加样式来定义各个状态的外观,也能够经过Redefinition level来改变或者彻底重写Block的行为
把代码分离到各文件中,遵循文件目录结构规则
规则:
每一个Block的逻辑和可选的Element以及Modifier都放在单独文件里
每一个组件的JS文件目录结构遵循BEM文件目录结构规则
示例:
logo/ logo.css # Block’s appearance logo.tmpl # Templates for generating the block’s HTML representation logo.js # Dynamic behavior of the block in the browser
便于管理、复用、移植、支持Redefinition level和组合使用
把逻辑分离到各个Redefinition level
在不一样的Redefinition level实现新Block能够继承并扩展示有的Block,或者彻底重写该Block,例如:
// 彻底重写 block('button').onSetMod({ focused: { true: this.someCustomOnFocused } }); // 部分重写 block('button').onSetMod({ focused: { true: function() { this.__base.apply(this, arguments); this.someCustomOnFocused(); } } });
原则
JS中的Block与DOM节点没有强对应关系,好比有的Block没有DOM表现,只是单纯的逻辑块
Block之间的交互经过事件订阅来实现,好比:
订阅其它Block实例的事件
订阅Modifier变动
直接调用其它Block的公开接口
其它任何交互模式,好比Event Channel(消息机制)
Element只能经过所属的Block提供的API来访问,不能直接访问某个Block中的Element
五.文件目录结构
代码被分解成独立的小部分,便于开发独立Block,发布前组装起来进行优化
特色
把Block implementation分解到各个单独的文件中
便于管理(开发过程当中)和移植(植入其它项目)
可选的Element和Modifier都存放在各个单独的文件中
只引入相关的Block implementation(按需加载)
文件按语义分组,而不是文件类型
好比全部Block都放在
block
目录下,以保证只引入须要的Block项目被划分红各Redefinition level
为了消除重复代码,便于重写/扩展示有Library Block,当Library Block更新时,不会影响上层的重写/扩展
目录结构示例
blocks/ input/ # input block directory _type/ # type modifier directory input_type_search.css # Implementation of modifier type # with value search in CSS technology __box/ # box element directory input__box.css input.css input.js button/ # button block directory button.css button.js button.png popup/ # popup block directory _target/ popup_target.css # Common code of modifier target popup_target_anchor.css # Modifier target with value anchor popup_target_position.css # Modifier target with value position _visible/ popup_visible.css # Boolean modifier visible popup.css popup.js
文件名也遵循BEM命名规则,Modifier之间的共享部分能够提出来,做为单独文件(popup_target.css
)
Redefinition level示例
library.blocks/ button/ button.css # CSS implementation in the linked library (height 20px) project.blocks/ button/ button.css # Redefinition of CSS implementation (height 24px)
文件存放在不一样的层级上,分离开避免互相影响
或者按平台分层:
common.blocks/ button/ button.css # Generic CSS implementation of the button desktop.blocks/ button/ button.css # Desktop platform-specific button features mobile.blocks/ button/ button.css # Mobile platform-specific button features
build过程会自动按层级引入,例如:
@import(common.blocks/button/button.css); /* Generic CSS rules */ @import(desktop.blocks/button/button.css); /* Desktop platform-specific */
六.build
BEM项目都有多级文件结构,build完成的工做是:
把Block相关的单独文件整合起来
按需引入Block、Element和Modifier
确保按正确顺序引入(保证Redefinition level)
固然,须要预先定义一些依赖信息,好比:
建立页面时列出相关的Block、Element和Modifier
指明页面中的Block、Element和Modifier的依赖关系
依赖声明能够经过工具完成,好比可以自动根据HTML中应用的class,生成BEM Tree,或者根据项目结构生成依赖信息(非按需生成,全部东西都会被包进来,适用于肯定项目目录下全部东西都是必须的状况,例如类库),关于依赖声明方式的更多信息,请查看Declarations in BEM
依赖引入方案与实现有关,好比,CSS用@import
,JS用AMD等方案
build结果
// build前 blocks/ # Directory containing the blocks bundles/ # Directory containing all build results hello/ # Directory for the hello page hello.decl.js # List of BEM entities required for the hello page An example of a post-build file structure of a BEM project: // build后 blocks/ # Directory containing the blocks bundles/ # Directory containing all build results hello/ # Directory for the hello page hello.decl.js # List of BEM entities required for the hello page hello.css # Built CSS file for the hello page hello.js # Built JavaScript file for thehello page
上例中hello.decl.js
中定义了依赖的组件及其依赖关系
build tool
官方提供的build工具是ENB,听说很强大:
The BEM platform uses ENB, which is a tool suitable for building BEM projects of any level of complexity.
想要尝试请查看新手教程:Starting your own BEM project
参考资料
https://en.bem.info/
(居然发现了师父,之后发布前得预处理一下了。>_<)
本文分享自微信公众号 - 前端向后(backward-fe)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。