element-plus 架构 - BEM和命名空间的实现
1,什么是BEM
对于一个前端开发团队来说,随着项目的规模越来越大,良好的代码规范越显得重要。
在 CSS 命名方面,经常会遇到的问题:
- 绞尽脑汁的去想一个 class,还得和其他类似的 class 做区分。
- 修改旧代码时,得去仔细确认每个 class 的作用,是修改、删除,还是添加一个新的 class 覆盖。
- 协作开发时 class 命名的冲突。
BEM 可以解决这些问题,让代码更容易阅读和控制,规范团队的代码风格。
1.1,介绍
BEM 是一种CSS命名规范。由 B(block)模块,E(element)元素,M(modifier)修饰符组成。
书写规范:
- 连接 element 用 __双下划线
- 连接 modifier 用 --双中划线
- 1个 class 中,B、E、M 尽量都只有1个(比如:尽量避免block__el1__el2)
具体表现如下:1
2
3
4.block {}
.block__element {}
.block__element--modifier {}
.block--modifier {}
- 在前端项目中,一般是由多个组件构成的,组件就是一个模块 block。
- element 表示 block 内更细粒度的元素,一般以布局或功能区分这些元素。
- modifier 用于标记 block 或 element 的不同版本或状态。
1.2,举例
用例:弹出框组件 dialog
1 | <div class="dialog center"> |
这样的命名方式,在阅读时并不能确定:
- center是一个通用的 class,还是只对 dialog 生效。
- body等是否也只在 dialog 中用到。如果body 中又嵌套了一个组件,也有类似body 的结构,命名就有点麻烦了。
BEM改造:1
2
3
4
5
6
7
8<div class="dialog dialog--center">
<div class="dialog__header">
<div class="dialog__title"></div>
<div class="dialog__closebtn"></div>
</div>
<div class="dialog__body"></div>
<div class="dialog__footer"></div>
</div>
1.4,总结
可以看到通过BEM规范化后,代码更易阅读和控制:
- 增加了代码的自解释性,能够直观的看到层级和依赖关系。
- class 单一职责。
- 避免了命名污染(污染外层或公共的同名 class)。
2.使用用法
这里用预编译器 sass 举例,其他预编译器用法类似。
安装 sass:1
npm install sass
使用sass 最小单元复刻一个bem 架构1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38$block-sel: "-" !default;
$element-sel: "__" !default;
$modifier-sel: "--" !default;
$namespace:'xm' !default;
@mixin bfc {
height: 100%;
overflow: hidden;
}
//混入
@mixin b($block) {
$B: $namespace + $block-sel + $block; //变量
.#{$B}{ //插值语法#{}
@content; //内容替换
}
}
@mixin flex {
display: flex;
}
@mixin e($element) {
$selector:&;
@at-root {
#{$selector + $element-sel + $element} {
@content;
}
}
}
@mixin m($modifier) {
$selector:&;
@at-root {
#{$selector + $modifier-sel + $modifier} {
@content;
}
}
}
在vite.config.ts 中引入1
2
3
4
5
6
7
8
9
10
11
12
13
14import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
css: {
preprocessorOptions: {
scss: {
additionalData: "@import './src/bem.scss';"
}
}
}
})
Vue 组件用法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30<template>
<div class="xm-wraps">
<div>
<Menu></Menu>
</div>
<div class="xm-wraps__right">
<Header></Header>
<Content></Content>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive } from "vue"
import Menu from './Menu/index.vue'
import Content from './Content/index.vue'
import Header from './Header/index.vue'
</script>
<style lang="scss" scoped>
@include b('wraps'){
@include bfc;
@include flex;
@include e(right){
flex:1;
display: flex;
flex-direction: column;
}
}
</style>