element-plus 中的 sass(2)

181 阅读2分钟

今天研究一下 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,只是作为一个容器来使用

主要来看em,他们都用了一个相似的写法,以 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