Skip to content

CSS 架构

结构

bash
   ├── ui
   ├── public
   ├── packages(最后把要打包的文件都放到这里面)
   ├── command
   ├── lib (打包后生成的)
   ├── vite.config.ts
   ├── package.json
   ├── packages
    ├── chooseButton
    ├── hooks
    ├── utils
    ├── styles
    ├── index.ts
    ├── vue.d.ts
   ├── src
    ├── hooks
    ├── styles
     ├── index.scss
     ├── chooseButton
     ├── index.scss

命名规范

  • css 命名规范遵循BEM规范

BEM 是 Block(块)、Element(元素)、Modifier(修饰符) 的缩写。它是一种由 Yandex 团队提出的 CSS 命名方法论。

遵循 BEM 规范的主要目的是:通过创建清晰、严格关联的命名规则,使代码更具可读性、可复用性,并避免样式冲突。

Block(块)

  • 定义:块是一个独立的实体,它本身具有意义,而且是页面结构的抽象组件。

  • 特点:

  1. 可以嵌套(例如,一个“头部”块里可能包含一个“Logo”块)。

  2. 不依赖于页面上的其他块或元素(即你把它放在页面的任何位置,它的外观和行为都应该是一样的)。

  3. 命名:使用类名,通常是一个单词或短语。

  • 示例:.card, .menu, .btn, .header

Element(元素)

  • 定义:元素是块的组成部分,它没有独立的含义,且在语义上与块相关联。

  • 特点:

  1. 依赖于块(不能脱离块单独使用)。

  2. 命名:块名 + 双下划线 __ + 元素名。

  • 示例:.card__title, .menu__item, .btn__text, .header__logo

Modifier(修饰符)

  • 定义:修饰符用于改变块或元素的外观、行为或状态。

  • 特点:

  1. 它是可选的。

  2. 命名:块名/元素名 + 双连字符 -- + 修饰符名。

  • 示例:.card--featured, .menu--vertical, .btn--disabled, .header--dark

命名规则图解

假设我们要写一个搜索框组件

  • Block: .search-form (搜索表单块)
  • Element: .search-form__input (输入框元素)
  • Element: .search-form__button (按钮元素)
  • Modifier: .search-form--theme-dark (深色主题修饰符)

创建生成class文件

  • hooks/usernamespace/index.ts
ts
// 默认的命名空间

export const defaultNamespace = "Y";

/*
 * 通过bem的方法生成所有的命名规则
 */

const _bem = (
  namespace: string,
  block: string,
  element?: string,
  modifierAttr?: string,
  modifierValue?: string,
): string => {
  // block
  let classname = `${namespace}-${block}`; // 结果Y-Buton
  // element
  element && (classname += `__${element}`); // 结果Y-Button__icon
  // modifier
  modifierAttr && (classname += `--${modifierAttr}`); // 结果Y-Button--disabled
  // modifierValue
  modifierValue && (classname += `_${modifierValue}`); // 结果Y-Button--disabled_disabled
  return classname;
};

export const useNamespace = (block: string) => {
  // 默认
  const namespace = defaultNamespace;
  // block
  const b = () => _bem(namespace, block);
  // element
  const e = (element: string | undefined) =>
    element ? _bem(namespace, block, element) : "";
  // modifier
  const m = (
    modifierAttr: string | undefined,
    modifierValue?: string | undefined,
  ) =>
    modifierAttr ? _bem(namespace, block, "", modifierAttr, modifierValue) : "";
  // is
  const is = (name: string, state: boolean) => {
    return name && state ? `is-${name}` : "";
  };
  return {
    is,
    namespace,
    b,
    e,
    m,
  };
};

使用

vue
<template>
  <div>
    <button
      :class="[
        ns.b(),
        ns.m(props.type),
        ns.m('size', props.size),
        ns.is('round', props.round),
        ns.is('disabled', props.disabled),
        ns.is('text', props.text),
        ns.is('link', props.link),
        ns.is('block', props.block),
        ns.is('border', props.border),
        ns.is('circle', props.circle),
      ]"
      :disabled="disabled"
      @click="handleClick"
    >
      <span>测试1</span>
    </button>
  </div>
</template>

<script setup lang="ts">
import { onMounted } from "vue";
import { useButton, useNamespace } from "../../../hooks/index";

const props = defineProps({
  type: {
    type: String,
    default: "",
  },
  text: {
    type: Boolean,
    default: false,
  },
  round: {
    type: Boolean,
    default: false,
  },
  disabled: {
    type: Boolean,
    default: false,
  },
  link: {
    type: Boolean,
    default: false,
  },
  block: {
    type: Boolean,
    default: false,
  },
  border: {
    type: Boolean,
    default: false,
  },
  size: {
    type: String,
    default: "", // 小small,中 就不用写 大large
  },
  circle: {
    type: Boolean,
    default: false,
  },
});

const ns = useNamespace("button");

console.log("useNameSpace", ns.b());

const { handleClick } = useButton();
onMounted(() => {
  console.log("按钮组件挂载");
});
</script>