今天研究一下 element-plus中的BEM写法
用法
sass 定义
// 文件下`theme-chalk-mixins`
@use 'mixins/mixins' as *;
@use 'common/var' as *;
@include b(carousel){
@include e(item){
position: absolute;
// xxxx
@include m(card) {
width: 50%;
// xx
}
}
}
模板使用
<template>
<!-- 父元素 -->
<div :class="[ns.b(),ns.m('card')]">
<!-- 子元素 -->
<div :class="[
ns.e('item'),
ns.is('active', true),
]">
xxx
</div>
</div>
</template>
<script lang="ts" setup>
const ns = useNamespace('carousel')
</script>
在模板中使用useNamespace,形成一个单独的作用域ns
使用ns.b或者ns.m / ns.e设置样式
使用ns.is设置动态样式
解析
解析sass
在文件夹 packages\theme-chalk\src\mixins\mixins.scss
// B
@mixin b($block) {
// 全局可以使用
$B: 'el-' + $block !global;
.#{$B} {
@content;
}
}
// E
@mixin e($element) {
$E: $element !global;
$selector: &;
$currentSelector: '';
// 遍历 传入的 $element,然后不断的拼接
@each $unit in $element {
$currentSelector: #{$currentSelector +
'.' +
$B +
$element-separator +
$unit +
','};
}
}
// M
@mixin m($modifier) {
$selector: &;
$currentSelector: '';
@each $unit in $modifier {
$currentSelector: #{$currentSelector +
$selector +
$modifier-separator +
$unit +
','};
}
@at-root {
#{$currentSelector} {
@content;
}
}
}
@mixin b 是一个简单的mixin,只是作为一个容器来使用
主要来看e 和 m,他们都用了一个相似的写法,以 e 为例
// E
@mixin e($element) {
$E: $element;
$selector: &;
$currentSelector: '';
// 本质是一个递归的过程
// . 是class
// $currentSelector 是 原来的结果 .item,然后加上新的 class
// 只能拼接',',而且最后一个还是会去掉
@each $unit in $element {
$currentSelector: #{$currentSelector +
'.' +
$B +
$element-separator +
$unit +
','
};
}
@at-root {
#{$currentSelector} {
@content;
}
}
}
简单来说,是一个递归的过程,通过遍历传入的$element元素,获得$unit,$currentSelector由最开始的 空字符不断的累加,最终形成一个新的字符串,形成类似于.item1,.item2,(尾部有逗号)
解析 useNamespace
在文件 packages\hooks\use-namespace\index.ts
const _bem = (
namespace: string,
block: string,
blockSuffix: string,
element: string,
modifier: string
) => {
// namespace 可以理解为固定字符 el
let cls = `${namespace}-${block}`
if (blockSuffix) {
cls += `-${blockSuffix}`
}
if (element) {
cls += `__${element}`
}
if (modifier) {
cls += `--${modifier}`
}
return cls
}
const useNamespace = (block: string) => {
const namespace = "el";
const statePrefix = 'is-'
const b = (blockSuffix = '')=>{
return _bem(namespace, block, blockSuffix, '', '')
};
const e = (element?: string) => {
return element ? _bem(namespace, block, '', element, '') : ''
}
const em = (element?: string, modifier?: string) =>
element && modifier
? _bem(namespace, block, '', element, modifier)
: ''
const is: {
(name: string, state: boolean): string
(name: string): string
} = (name: string, args:boolean | void ) => {
// 如果有多个条件的话
const state = args ?? true
return name && state ? `${statePrefix}${name}` : ''
}
return {
b,
e,
em,
is
}
}
const ns = useNamespace('carousel');
// "el-carousel"
console.log(ns.b()) // "el-carousel"
console.log(ns.e('mask')) // "el-carousel__mask"
console.log(ns.em('item', 'card')) // "el-carousel__item--card"
console.log(ns.is("c",true)) // 'is-c'
namespace 可以作为一个普通的字符el,主要是对函数_bem传入不同的参数返回不同的结果
is这个方法是一个函数重载,如果传入一个参数的话,直接使is-${name}
传入第二个参数的话,可以根据第二个参数来判断是否是is-${name}还是''
总结
不论是sass 中的bem 还是 ts 使用useNamespace的制作的局部作用域,都是为了代码结构清晰,规范,易于维护,虽说不难,但是好的项目都是从细节不断的优化,从局部到整体
time:2022/12/14