일.BEM 简介
BEM 은 프론트엔드 개발 방법론 (Frontend Development Methodology) 을 자칭하며, 명명 규칙, CSS/JS 모듈화 원칙, 빌드 도구를 포함한개발环节용 일련의 방법을 제공합니다
P.S. 개발环节을 강조하는 것은 Yeoman 등의 것과 구분하기 위한 것으로, BEM 이 말하고자 하는 것은 방법이 아니라 도구입니다
일련의 명명 규칙 (BEM 명명 규칙) 을 제공하며, 주로 모듈화 CSS 에 사용되지만, BEM 에서는 JS 및 파일 명명 등 각 방면에서도 사용됩니다
CSS 에서는 주로 다음과 같은 문제를 해결했습니다:
- 팀 협력 중 스타일 명명 (예: class) 충돌
긴 class 이름으로 해결하며, 구조화 셀렉터 (자식 셀렉터, 자손 셀렉터 등) 를 사용하지 않고, 클래스 이름 자체에 계층 관계를 갖게 합니다
- 코드 자체가 말하는 (self-documenting code) 라는 목표 실현
의미론적 클래스 이름으로, 요소명, 기능, 소속 컴포넌트명 등 더 많은 정보를 제공합니다
- 컴포넌트 간 상호 영향을 회피
구조화 셀렉터에 의존하지 않고, 모두 클래스 이름에 의존합니다
- 동일 DOM 노드에서 각종 특성을 组装하면서 중복 코드 (복사 붙여넣기) 를 회피하는 방법
Mix, 예를 들어 div.classA>div.classB 로 Block A 와 Block B 를 組合せます
이러한 해결책은 [如何写好 CSS](/articles/如何写好 css/) 에서 설명되어 있습니다. 긴 클래스 이름 형식의 세트만 정의했다면, 확실히 의미는 없습니다. 따라서 BEM 은 이 명명 규칙을 위해 프레임워크와 도구도 구현하여, 개발 과정에서 사용할 수 있도록 했습니다
이.용어 개념
(건너뛰고 빨간 부분을 보는 것을 권장합니다. 공식 설명은 설명하지 않은 것과 같습니다)
###Block
로직과 페이지 기능 모두가 독립된 페이지 컴포넌트로, 재사용 가능한 유닛입니다. 특징은 다음과 같습니다:
-
자유롭게 네스트하여 組合셈 가능
-
임의의 페이지의 임의의 위치에 배치 가능하며, 기능이나 외관에 영향을 주지 않습니다
-
재사용 가능하여, 인터페이스에 동일 Block 의 인스턴스를 임의로 여러 개 가질 수 있습니다
###Element
Block 의 구성 부분으로, Block 에 의존하여 존재합니다 (Block 을 벗어나면 사용할 수 없습니다)
###Modifier
[옵션] Block 과 Element 의 외관 및 동작을 정의하며, HTML 속성처럼, 동일 종류의 Block 을 다르게 보이게 할 수 있습니다
###BEM entity
위 3 개 모두
###Mix
단일 DOM 노드에서 각 BEM entity 가 구성하는 1 개의 인스턴스로, 특징은 다음과 같습니다:
-
몇 개의 BEM entity 의 동작, 스타일을 결합할 수 있어, 코드 중복을 회피
-
기존 BEM entity 에 기반하여 의미론적으로 새로운 인터페이스 컴포넌트 생성
###BEM tree
BEM 을 사용하여 web 페이지 구조를 기술
###Block implementation
많은 다른 기술이 BEM entity 의 동작, 외관, 테스트, 템플릿, 문서, 의존 관계 기술, 추가 데이터 (이미지 등) 등의 측면을 결정
###Implementation technology
1 개의 Block 을 구현하는 데 사용하는 기술로, 1 종 또는 여러 종 가능
###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 의 외관
logo.tmpl # Block 의 HTML 표현을 생성하기 위한 템플리트
logo.js # 브라우저에서의 Block 의 동적 동작
관리, 재사용, 이식, 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 노드에는 강대응 관계가 없습니다. 예를 들어, DOM 표현을 갖지 않는 Block 도 있으며, 단순한 로직 블록입니다
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
###참고 자료
아직 댓글이 없습니다