基于函数式编程封装BEM

74 阅读2分钟

Bem设计

前置概念

BEM,全称Block Element Modifier,是一种前端编码规范,由Yandex团队提出,主要用于命名HTML和CSS中的类和选择器。BEM的核心思想是将页面拆分成一个个独立的富有语义的块(blocks),以提升开发效率并有利于代码复用。

在具体的命名规则中,BEM包括三部分:块(block)、元素(element)、修饰符(modifier)。其中,“-”仅作为连字符使用,用于连接块或子元素的多单词。这种命名方式旨在提供一种一致的方式来组织和命名代码,使其易于理解、扩展和维护。

此外,BEM也是一种保证CSS样式不冲突的方法。通过使用组件名、元素名和修饰器名来命名元素,可以避免样式与外部组件的冲突。总体来说,BEM规范对于提高代码质量、降低维护成本以及增强团队协作有着显著的作用。

createBem介绍

但每次都要长长的class类名又相对麻烦,修饰符上的书写也不是那么的方便。因此,createBem就应声而出。createBem是css 类名 bem 规范的构造函数。目的是为了解决如下问题:

  • 手动拼接 BEM 类名容易出错(如忘记 __--)。
  • 动态类名管理复杂(如条件修饰符 { active: isActive })。
  • 减少重复代码,提高可维护性。

调用示例

在js中使用:

const bem = createBem("v-button"); // 生成构造函数,命名空间为 button
bem(); // 'v-button' - 返回前缀+命名空间
bem("icon"); // 'v-button__icon' - 子元素
bem(":disabled"); // 'v-button--disabled' - 冒号代表修饰符
bem({ primary: true }); // 'v-button v-button--primary' - 对象类型代表可选修饰符
bem({ primary: true, focus: true, plain: false }); // 'v-button v-button--primary v-button--focus' - 多个可选修饰符
bem('name', { primary: true }) // v-button__name v-button__name--primary

在vue中使用:

<template>
    <div :class="bem()">
        <div :class="bem('title')">
            title    
        </div>
        
        <div :class="bem('name', { active: divActive })">
            content    
        </div>
    </div>
</template><script setup>
const bem = createBem('x-div')
​
const divActive = ref(true)
​
setTimeout(() => {
  divActive.value = false
}, 2000);
</script><style lang="less">
.x-div {
    &__title {
        color: red;
    }       
    
    &__name {
        color: yellow;
    
        &--active {
            background: blue;
        }
    }
}
</style>
​
// 编译结果
<template>
    <div class="x-div">
        <div class="x-div__title">
            title    
        </div>
        
        <div class="x-div__name x-div__name--active">
           content 
        </div>
    </div>
</template>

核心代码

const createBem = (name) => {
    const basicName = name || ''
    
    const bemFunc = (...rest) => {
        if(!rest.length) return basicName
        return rest.reduce((pre, ele) => {
            if(typeof ele === 'object') {
                pre = handleObject(pre, ele)
            } else {
                pre = handleString(pre, ele)
            }
            return pre
        }, basicName)
    }
    
    const handleObject = (pre, obj) => {
        let ret = pre
        Object.keys(obj).forEach(k => {
            if(obj[k]) {
                ret += ` ${pre}--${k}`
                // 对象的拼接是修饰符,需要叠加前面的块和元素
            }
        })
        return ret
    }
    
    const handleString = (pre, str) => {
        let ret = pre
        // 约定俗称 : 来代表修饰符
        if(str.startsWith(':')) {
            ret += `--${str.slice(1)}`
        } else {
            ret += `__${str}`
        }
        return ret
    }
    
    return bemFunc
}

总结

BEM 是一种流行的 CSS 命名方法论,主要用于大型项目,帮助开发者编写可维护、可扩展的样式代码。通过函数式编程,将 BEM 规范封装为简洁的 API,解决了 CSS 类名动态管理的问题,适合追求结构化和可维护性的前端项目。