vue3中应用BEM规范,使用sass函数拼接BEM类名,同时导出sass变量在js中访问

css中的BEM规范

bem是css中一种给类命名的规范,elementui就是按照bem规范给组件命名的。bem含义:

  • Block 代表一个块,例如一个input组件,就是一个组件块,有层级用 - 分割:el-input
  • Element 代表某个块里的元素之一,如块的外壳部分,以 __ 与块间隔:el-input__wrapper
  • Modifier 用来修饰块或元素,代表它们的类型,以 -- 与被修饰块或元素间隔:el-buttom--primary
/* 块命名层级表示用 - */
.tag-block {}
/* 块里的元素表示所属块用 __ */
.tag-block__header {}
/* 元素的样式修改用 -- */
.tag-block__header--primary {}
.tag-block__bottom--blue {}

如果使用sass来编写类通常会使用嵌套来表示层级关系,但是编译符合bem规范的css类是无需嵌套的。

如,.tag-block .tag-block__header{ color:red; } ,类命名已经有层级信息了,无需再嵌套,应该直接写为.tag-block__header{ color:red; }

sass提供了@at-root来修饰类,可以在sass中表示类的层级关系,但把类编译成css时,会把它提到顶层,解除嵌套。

/*scss*/
.el-input {
  color: red;
  @at-root .el-input__wraper { color: green; }
}
/*编译为css*/
.el-input { color: red; }
.el-input__wraper { color: green; }

vue3项目中使用BEM规范

bem作为css规范,可在任意项目中使用。

由于bem规范中使用到了像- __ --,这些固定分割符号,为了提高复用性和规范性,本文会展示如何在vue3中,自定义sass函数,来为我们使用指定的分隔符进行拼接类名。

同时为了在js中同步sass这些类名,会配置sass变量的导出,和在js中导入这些变量。为了提高复用,在js同样封装了与sass函数功能一样的,拼接类名的函数,并挂载到vue实例上。

使用vite作为构建工具

定义sass函数,自动拼接为符合规范的类名

  1. 安装sass依赖 npm i sass -D;

  2. 使用sass提供的函数功能,定义拼接符合bem命名规范的sass函数

    // ./src/bem.scss
    
    $block-split: "-" !default; // 块分割符,!default代表无需访问其它变量,它就是一个独立的值
    $element-split: "__" !default; // 元素分割符
    $modifier-split: "--" !default; // 修饰分割符
    $namespace: 'el' !default; // 命名空间,通常是项目名
    
    
    //混入函数,自动拼接类名:el-xxx
    // xxx为$block对应的实参
    @mixin b($block) {
        $B: $namespace + $block-split + $block; //局部变量
        //插值语法#{}
        .#{$B} {
            @content; //内容替换
        }
    }
    /*
    * 使用示例
    * 
    * @include b(input) {
    *     color: green;
    * }
    * 编译为 .el-input {color: green;}
    */
    
    //混入函数,自动拼接类名,并与父级同级不嵌套:__xxx
    @mixin e($element) {
        // @at-root表示生成的类不会嵌套
        @at-root {
            // 拼接&,和上一层类进行类名拼接即:&__xxx
            #{& + $element-split + $element} {
                @content;
            }
        }
    }
    /*
    使用示例,(ps:必须嵌套在块内部):
    @include b(input) {
        e(wraper) {
            color: red;
        }
    }
    
    编译为:
    .el-input__wraper { color: red;}
    */
    
    //混入函数,自动拼接类名,并与父级同级不嵌套:--xxx
    @mixin m($modifier) {
    
        @at-root {
            #{& + $modifier-split + $modifier} {
                @content;
            }
        }
    }
    
  3. 为了无需手动在每个组件引入上述bem.css中的函数,可直接配置vite.config.css,来为每个组件引入bem.sass

    // ./vite.config.ts
    import { fileURLToPath, URL } from 'node:url'
    
    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    
    // https://vitejs.dev/config/
    export default defineConfig({
      plugins: [
        vue(),
      ],
      css: {
        preprocessorOptions: {
          scss: {
            // 为每个组件的scss类型的style标签引入./src/bem.scss
            additionalData: '@use "./src/bem.scss" as *;'
          }
        }
      },
      resolve: {
        alias: {
          '@': fileURLToPath(new URL('./src', import.meta.url))
        }
      }
    })
    
  4. 在组件里书写bem规范的类名

    <!-- ./src/component/DemoSass.vue -->
    <script setup lang="ts">
    import My from './components/My.vue'
    </script>
    
    
    <template>
      <My></My>
      <div class="el-title el-title--primary">
        <div :class="el-title__wraper">小明</div>
      </div>
    </template>
    
    <style lang="scss">
    @include b(title) {
      color: grey;
      @include e(wraper) {
        border: 1px solid black
      }
      @include m(primary) {
        background-color: #fff;
      }
    }
    /*
    编译结果
    .el-title {
      color: grey;
    }
    .el-title__wraper {
      border: 1px solid black;
    }
    .el-title--primary {
      background-color: #fff;
    }
    */
    </style>
    

将sass中的变量导出,在js中获取,并拼接为类名

sass代码中,可使用sass函数来灵活拼接类名,而标签中却要写死类名,书写sass函数的意义就不大了。

可以将sass中的变量导出,让js也能访问。在js中拼接为和sass中一致的类名。

  1. 使用:export 将sass变量导出

    // ./src/config.module.scss
    @use './bem.scss' as *; // 导入上文的bem.scss,这样就可以访问bem.scss里面定义的变量
    :export {
        b: $block-split;// $block-split是bem.scss中定义的变量
        e: $element-split;
        m: $modifier-split;
        namespace: $namespace;
    }
    
  2. js中导入sass导出的变量

    由于sass导出字符串的变量时,会把双引号带上,所以在js中使用时,要把双引号去掉

    // ./src/handleScssVar.ts 名字自定义即可
    import config from './config.module.scss';  // 导入sass中导出的变量
    const scssVarObj: any = Object.create(null);
    
    // 注意sass导出时,会把变量的双引号也带上:如 {b : "\"-\""},
    // sass会把双引号也当成变量的一部分导出来,这里把双引号去掉
    Object.keys(config).map((item: string) => scssVarObj[item] = config[item].slice(1, -1))
    
    export default scssVarObj; // 这样导出的对象,就是js中正常的值,如{b: '-'}
    
    // 或者直接导出几个函数,直接返回拼接好的类名
    
    /**
     * 
     * @param blockName 块的名称
     * @returns 返回命名空间拼接块名的类名,如:el-xxx
     */
    export function blockClass(blockName: string): string {
        return scssVarObj.namespace + scssVarObj.b + blockName
    }
    /**
     * 
     * @param blockName 块的名称
     * @param elementName 块内部元素的名称
     * @returns 依次拼接命名空间、块、块内元素的类名,如:el-xxx__wraper
     */
    export function elementClass(blockName: string, elementName: string): string {
        return blockClass(blockName) + scssVarObj.e + elementName
    }
    /**
     * 
     * @param blockName 块的名称
     * @param modifierName 修饰块的类型
     * @returns 依次拼接命名空间、块、块类型修饰的类名,如:el-xxx--primary
     */
    export function modifierClass(blockName: string, modifierName: string): string {
        return blockClass(blockName) + scssVarObj.m + modifierName
    }
    
    
  3. 为了不需要在每个vue文件中导入上面的js文件,就可以访问这些函数,可以把它们挂到vue实例上

    // ./main.ts 主入口文件
    import { createApp } from 'vue'
    import App from './App.vue'
    import { blockClass,elementClass,modifierClass } from './handleScssVar';
    
    const app = createApp(App);
    // 挂到vue实例上
    app.config.globalProperties.$blockClass = blockClass;
    app.config.globalProperties.$elementClass = elementClass;
    app.config.globalProperties.$modifierClass = modifierClass;
    app.mount('#app')
    
  4. 此时,通过this访问这些函数是可以访问的,但在ts中会报错,因为vue模块的ts声明文件没有这几个接口,需要我们声明一下

    // ./vueExtend.d.ts 
    // 防止覆盖全局。
    export {}
    
    declare module 'vue' {
      // 扩展vue模块上的接口
      interface ComponentCustomProperties {
        $blockClass: (key: string) => string;
        $modifierClass(blockName: string, modifierName: string): string;
        $elementClass(blockName: string, elementName: string): string
      }
    }
    
  5. 最后使用时,上面的组件可以改为

    <!-- ./src/component/DemoSass.vue -->
    <script setup lang="ts">
    import My from './My.vue'
    </script>
    
    <template>
      <My></My>
      <div :class="[$blockClass('title'), $modifierClass('title', 'primary')]">
        <div :class="$elementClass('title', 'wraper')">小明</div>
      </div>
    </template>
    
    <style lang="scss">
    @include b(title) {
      color: grey;
      @include e(wraper) {
        border: 1px solid black
      }
      @include m(primary) {
        background-color: #fff;
      }
    }
    /*
    编译结果
    .el-title {
      color: grey;
    }
    .el-title__wraper {
      border: 1px solid black;
    }
    .el-title--primary {
      background-color: #fff;
    }
    */
    </style>
    
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值