搭建vue3组件库(三): CSS架构之BEM

news2025/2/24 12:02:55

文章目录

    • 1. 通过 JS 生成 BEM 规范名称
      • 1.1 初始化 hooks 目录
      • 1.2 创建 BEM 命名空间函数
      • 1.3 通过 SCSS 生成 BEM 规范样式
    • 2. 测试 BEM 规范

BEM 是由 Yandex 团队提出的一种 CSS 命名方法论,即 Block(块)、Element(元素)和 Modifier(修改器)的简称,是 OOCSS 方法论的一种实现模式,底层仍然是面向对象的思想。

  • BEM 规范下 classname 的命名格式为:
    • 所有实体的命名均使用小写字母,复合词使用连字符 “-” 连接。
    • Block 与 Element 之间使用双下画线 “__” 连接。
    • Mofifier 与 Block/Element 使用双连接符 “–” 连接。
    • modifier-name 和 modifier_value 之间使用单下画线 “_” 连接。

1. 通过 JS 生成 BEM 规范名称

在编写组件的时候如果通过手写 classname 的名称,那么需要经常写 - 、 __ 、 --,那么就会变得非常繁琐,BEM 命名规范是具有一定规律性的,所以可以通过 JavaScript 按照 BEM 命名规范进行动态生成。

1.1 初始化 hooks 目录

packages 目录下创建一个 hooks 目录,进入到 hooks 目录底下初始化一个 package.json 文件,修改内容如下:

{
  "name": "@vision-ui-vue/hooks",
  "version": "0.0.1",
  "license": "MIT",
  "main": "index.ts",
  "module": "index.ts",
  "unpkg": "index.js",
  "jsdelivr": "index.js",
  "types": "index.d.ts",
  "peerDependencies": {
    "vue": "^3.2.0"
  }
}

1.2 创建 BEM 命名空间函数

hooks 目录下再创建一个 use-namespace 目录用于创建 BEM 命名空间函数,再在 hooks 目录下创建一个 index.ts 文件用于模块入口文件。

// index.ts
import * from './use-namespace'
// use-namespace/index.ts
import { computed, unref } from 'vue'

// 默认的命名空间
export const defaultNamespace = 'v'
// 状态前缀
const statePrefix = 'is-'

/**
 * BEM 命名字符拼接函数
 * @param namespace 命名空间
 * @param block 块名
 * @param blockSuffix 块的后缀
 * @param element 元素名
 * @param modifier 修改器名
 * @returns 拼接后的BEM类名字符串
 */
const _bem = (
  namespace: string,
  block: string,
  blockSuffix: string,
  element: string,
  modifier: string
) => {
  // 默认是 Block
  let cls = `${namespace}-${block}`
  // 如果存在 Block 后缀,也就是 Block 里面还有 Block
  if (blockSuffix) {
    cls += `-${blockSuffix}`
  }
  // 如果存在元素
  if (element) {
    cls += `__${element}`
  }
  // 如果存在修改器
  if (modifier) {
    cls += `--${modifier}`
  }
  return cls
}

/**
 * 用于创建和管理BEM类名的工具函数
 * @param block 块名
 * @returns 返回一个对象,包含用于创建BEM类名的各种方法
 */
export const useNamespace = (block: string) => {
  // 基于Vue的computed创建动态命名空间
  const namespace = computed(() => defaultNamespace)
  // 创建块级类名 v-form
  const b = (blockSuffix = '') =>
    _bem(unref(namespace), block, blockSuffix, '', '')

  // 创建元素级类名 v-input__inner
  const e = (element?: string) =>
    element ? _bem(unref(namespace), block, '', element, '') : ''

  // 创建修改器类名 v-form--default
  const m = (modifier?: string) =>
    modifier ? _bem(unref(namespace), block, '', '', modifier) : ''

  // 创建带后缀的块元素类名 v-form-item
  const be = (blockSuffix?: string, element?: string) =>
    blockSuffix && element
      ? _bem(unref(namespace), block, blockSuffix, element, '')
      : ''

  // 创建元素的修改器类名 v-scrollbar__wrap--hidden-default
  const em = (element?: string, modifier?: string) =>
    element && modifier
      ? _bem(unref(namespace), block, '', element, modifier)
      : ''

  // 创建块后缀的修改器类名 v-form-item--default
  const bm = (blockSuffix?: string, modifier?: string) =>
    blockSuffix && modifier
      ? _bem(unref(namespace), block, blockSuffix, '', modifier)
      : ''

  // 创建块元素的修改器类名 v-form-item__content--xxx
  const bem = (blockSuffix?: string, element?: string, modifier?: string) =>
    blockSuffix && element && modifier
      ? _bem(unref(namespace), block, blockSuffix, element, modifier)
      : ''

  // 创建动作状态类名,支持两种调用方式
  const is: {
    (name: string, state: boolean | undefined): string
    (name: string): string
  } = (name: string, ...args: [boolean | undefined] | []) => {
    const state = args.length >= 1 ? args[0]! : true
    return name && state ? `${statePrefix}${name}` : ''
  }

  return {
    namespace,
    b,
    e,
    m,
    be,
    em,
    bm,
    bem,
    is,
  }
}

1.3 通过 SCSS 生成 BEM 规范样式

packages/theme-chalk 目录下创建一个 src 目录,在 src 目录下创建一个 mixins 目录。在 mixins 目录下新建件:config.scssmixins.scssfunction.scss, config.scss 编写 BEM 的基础配置比如样式名前缀、元素、修饰符、状态前缀:

$namespace: 'v' !default; // 所有的组件以v开头,如 v-input
$common-separator: '-' !default; // 公共的连接符
$element-separator: '__' !default; // 元素以__分割,如 v-input__inner
$modifier-separator: '--' !default; // 修饰符以--分割,如 v-input--mini
$state-prefix: 'is-' !default; // 状态以is-开头,如 is-disabled
// 在 SCSS 中,使用 $+ 变量名:变量 来定义一个变量。在变量后加入 !default 表示默认值。给一个未通过 !default 声明赋值的变量赋值,此时,如果变量已经被赋值,不会再被重新赋值;但是如果变量还没有被赋值,则会被赋予新的值。

mixins.scss 编写 SCSS 的 @mixin 指令定义的 BEM 代码规范:

@use '../common/var' as *;

// 定义 Block
@mixin b($block) {
  $B: $namespace + $common-separator + $block !global;

  .#{$B} {
    @content;
  }
}

// 定义 Element
@mixin e($element) {
  $E: $element !global;
  $selector: &;
  $currentSelector: '';
  @each $unit in $element {
    $currentSelector: #{$currentSelector +
      '.' +
      $B +
      $element-separator +
      $unit +
      ','};
  }

  @if hitAllSpecialNestRule($selector) {
    @at-root {
      #{$selector} {
        #{$currentSelector} {
          @content;
        }
      }
    }
  } @else {
    @at-root {
      #{$currentSelector} {
        @content;
      }
    }
  }
}

// 定义修改器
@mixin m($modifier) {
  $selector: &;
  $currentSelector: '';
  @each $unit in $modifier {
    $currentSelector: #{$currentSelector +
      $selector +
      $modifier-separator +
      $unit +
      ','};
  }

  @at-root {
    #{$currentSelector} {
      @content;
    }
  }
}

// 定义动作状态
@mixin when($state) {
  @at-root {
    &.#{$state-prefix + $state} {
      @content;
    }
  }
}

function.scss 定义一些 SCSS 的 @function 指令定义的函数:

@use 'config';

// 该函数将选择器转化为字符串,并截取指定位置的字符
@function selectorToString($selector) {
  $selector: inspect(
    $selector
  ); // inspect(...) 表达式中的内容如果是正常会返回对应的内容,如果发生错误则会弹出一个错误提示。
  $selector: str-slice($selector, 2, -2); // str-slice 截取指定字符
  @return $selector;
}
// 判断父级选择器是否包含'--'
@function containsModifier($selector) {
  $selector: selectorToString($selector);

  @if str-index($selector, config.$modifier-separator) {
    // str-index 返回字符串的第一个索引
    @return true;
  } @else {
    @return false;
  }
}
// 判断父级选择器是否包含'.is-'
@function containWhenFlag($selector) {
  $selector: selectorToString($selector);

  @if str-index($selector, '.' + config.$state-prefix) {
    @return true;
  } @else {
    @return false;
  }
}
// 判断父级是否包含 ':' (用于判断伪类和伪元素)
@function containPseudoClass($selector) {
  $selector: selectorToString($selector);

  @if str-index($selector, ':') {
    @return true;
  } @else {
    @return false;
  }
}
// 判断父级选择器,是否包含`--` `.is-`  `:`这三种字符
@function hitAllSpecialNestRule($selector) {
  @return containsModifier($selector) or containWhenFlag($selector) or
    containPseudoClass($selector);
}

2. 测试 BEM 规范

  • 在根目录执行 pnpm install sass -D -w
  • 接着执行 pnpm install @vision-ui-vue/hooks -D -w 把 hooks 引入到项目中
  • packages/components 下新建 button 目录,目录结构为
├── packages
│   ├── components
│   │   ├── button
│   │   │   ├── src
│   │   │   │   └── button.vue
│   │   │   └── index.ts
│   │   └── package.json

index.ts 内容:

import Button from './src/button.vue'
export default Button

button.vue 内容:

<template>
  <button :clsaa="bem.b()">测试按钮</button>
</template>

<script lang="ts" setup>
import { useNamespace } from '@vision-ui-vue/hooks'
const bem = useNamespace('button')
</script>
  • 进入 play 项目,修改 App.vue 文件,引入 button 组件:
<template>
  <div>
    <v-button></v-button>
  </div>
</template>

<script setup lang="ts">
import VButton from '@vision-ui-vue/components/button'
</script>

  • 运行 play 项目,查看效果
    打开控制台,可以看到按钮的class为 v-button
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1638669.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WORD排版常见问题与解决方案

前言 近期使用word软件进行论文排版工作&#xff0c;遇到了一些常见的问题&#xff0c;记录一下&#xff0c;避免遗忘。 基本配置 系统环境&#xff1a;win10/win11 word版本&#xff1a;Microsoft Office LTSC 专业增强版 2021 问题与解决方案 问题1&#xff1a;页眉显示内…

C——双向链表

一.链表的概念及结构 链表是一种物理存储单元上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的。什么意思呢&#xff1f;意思就是链表在物理结构上不一定是连续的&#xff0c;但在逻辑结构上一定是连续的。链表是由一个一个的节点连…

使用递归函数,将一串数字每位数相加求和

代码结果&#xff1a; #include<stdio.h> int DigitSum(unsigned int n) {if (n > 9)return DigitSum(n / 10) (n % 10);elsereturn n; } int main() {unsigned int n;scanf("%u", &n);int sum DigitSum(n);printf("%d\n", sum);return 0; …

持续更新|UNIAPP适配APP遇到的问题以及解决方案

在使用UNIAPP开发APP的时候遇到的一些奇奇怪怪问题记录 组件样式丢失 问题&#xff1a;组件引入界面中&#xff0c;在小程序和H5环境下样式正常&#xff0c;而在APP中却出现高度异常问题 解决&#xff1a;增加view标签将组件包裹起来即可正常显示 解决前&#xff1a; 解决后…

SCI一区 | MFO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测(Matlab)

SCI一区 | MFO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测&#xff08;Matlab&#xff09; 目录 SCI一区 | MFO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测&#xff08;Matlab&#xff09;预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现MFO-CNN…

JAVA第二周学习笔记

文章目录 JAVA第二周学习笔记IDEA方法格式带参数及返回值的方法方法的重载方法的内存 二维数组静态初始化动态初始化 面向对象类和对象如何定义类如何得到对象注意 封装封装的优点private关键字成员变量和局部变量 this关键字构造方法作用类型特点执行时机定义重载 标准javabea…

Linux进程——进程的创建(fork的原理)

前言&#xff1a;在上一篇文章中&#xff0c;我们已经会使用getpid/getppid函数来查看pid和ppid,本篇文章会介绍第二种查看进程的方法&#xff0c;以及如何创建子进程&#xff01; 本篇主要内容&#xff1a; 查看进程的第二种方法创建子进程系统调用函数fork 在开始前&#xff…

什么是哈希表(HashTable)?

目录 一、概念 二、哈希冲突 减少哈希冲突的办法&#xff1a; 1、设计合理的哈希函数 哈希函数设计原则&#xff1a; 常用的哈希函数&#xff1a; 2、降低负载因子&#xff08;必须重点掌握&#xff09; 哈希冲突的解决 第一类&#xff1a;闭散列 第二类&…

实时监控RTSP视频流并通过YOLOv5-seg进行智能分析处理

在完成RTSP推流之后&#xff0c;尝试通过开发板接收的视频流数据进行目标检测&#xff0c;编写了一个shell脚本实现该功能&#xff0c;关于视频推流和rknn模型的部署请看之前的内容或者参考官方的文档。 #!/bin/bash # 设置脚本使用的shell解释器为bashSEGMENT_DIR"./seg…

【模板】前缀和

原题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 前缀和模板题。 前缀和中数组下标为1~n。 前缀和&#xff1a;pre[i]pre[i-1]a[i]; 某段区间 [l,r]的和&#xff1a;pre[r]-pre[l-1] 3.…

【数学建模】2024五一数学建模C题完整论文代码更新

最新更新&#xff1a;2024五一数学建模C题 煤矿深部开采冲击地压危险预测&#xff1a;建立基于多域特征融合与时间序列分解的信号检测与区间识别模型完整论文已更新 2024五一数学建模题完整代码和成品论文获取↓↓↓↓↓ https://www.yuque.com/u42168770/qv6z0d/gyoz9ou5upv…

unity制作app(2)--主界面

1.先跳转过来&#xff0c;做一个空壳&#xff01;新增场景main为4号场景&#xff01; 2.登录成功跳转到四号场景&#xff01; 2.在main场景中新建canvas&#xff0c;不同的状态计划用不同的panel来设计&#xff01; 增加canvas和底图image 3.突然输不出来中文了&#xff0c;浪…

【19-文本数据处理:Scikit-learn中的自然语言处理技术】

文章目录 前言理解文本数据文本预处理文本清洗分词停用词去除向量化文本数据词袋模型TF-IDF变换构建文本分类模型模型评估与调优结论前言 欢迎回到我们的Scikit-learn系列,在这篇文章中,我们将探讨如何使用Scikit-learn来处理文本数据,这是自然语言处理(NLP)的基础。你将学…

为家庭公网IP配置DDNS域名

文章目录 域名配置域名更新frp配置修改 在成功完成frp改造Windows笔记本实现家庭版免费内网穿透之后&#xff0c;某天我突然发现内网穿透失效了&#xff0c;一番排查之后原来是路由器对应的公网IP更换了。果然我分到的并不是固定的公网IP&#xff0c;而是会定期变化的。为了免受…

中间件之异步通讯组件RabbitMQ入门

一、概述 微服务一旦拆分&#xff0c;必然涉及到服务之间的相互调用&#xff0c;目前我们服务之间调用采用的都是基于OpenFeign的调用。这种调用中&#xff0c;调用者发起请求后需要等待服务提供者执行业务返回结果后&#xff0c;才能继续执行后面的业务。也就是说调用者在调用…

解决IDEA下springboot项目打包没有主清单属性

1.问题出现在SpringBoot学习中 , 运行maven打包后无法运行 报错为spring_boot01_Demo-0.0.1-SNAPSHOT.jar中没有主清单属性 SpringBoot版本为 2.6.13 Java 版本用的8 解决方法 1.执行clean 删除之前的打包 2.进行打包规范设置 2.1 3.进行问题解决 (借鉴了阿里开发社区) 使用…

OpenCV(二)—— 车牌定位

从本篇文章开始我们进入 OpenCV 的 Demo 实战。首先&#xff0c;我们会用接下来的三篇文章介绍车牌识别 Demo。 1、概述 识别图片中的车牌号码需要经过三步&#xff1a; 车牌定位&#xff1a;从整张图片中识别出牌照&#xff0c;主要操作包括对原图进行预处理、把车牌从整图…

碳纤维复合材料的纳米纤维膜

碳纤维复合材料的纳米纤维膜是一种具有良好性能和应用前景的新材料。以下是关于这种材料的详细介绍&#xff1a; 制备方法&#xff1a;碳纤维复合材料的纳米纤维膜可以通过多种方法制备&#xff0c;包括化学气相沉积法、固相合成法、模板法等。其中&#xff0c;化学气相沉积法是…

Docker——部署LNMP架构

目录 一、LNMP架构概述 1.项目环境 2.服务器环境 3.需求 二、搭建Linux系统基础镜像 三、部署Nginx 1.建立工作目录 2.编写Dockerfile脚本 3.准备Nginx.conf配置文件 4.生成镜像 5.创建自定义网络 6.启动镜像容器 7.验证Nginx 三、部署Mysql 1.建立工作目录 2.编…

MathType打开的窗口太多 MathType说打开窗口太多无法复制怎么解决

在数学文档编辑中&#xff0c;MathType作为一款常用的数学公式编辑工具&#xff0c;使用过程中&#xff0c;我们常常会遇到一些问题&#xff0c;比如MathType打开的窗口过多导致软件运行缓慢甚至崩溃&#xff0c;以及在复制过程中出现“打开窗口太多&#xff0c;无法复制”的提…