今天研究一下 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