Skip to content

样式

项目结构

项目中的样式文件存放在 src/common/styles,包含一些全局样式、ElementPlus 重构样式等。

Scss

项目中使用 scss 作为样式预处理器,可以在项目中使用 scss 的特性,如变量、函数、混合等。

vue
<style lang="scss" scoped>
$font-size: 30px;

.demo {
  color: green;
  font-size: $font-size;
}
</style>

BEM 命名空间规范

Teek 的组件基本采用 BEM 命名空间规范,命名空间为 tk,可以在 src/common/styles/var/namespace.scss 中查看命名空间或修改。

什么是 BEM

Teek 使用 BEM 规范进行样式编写,并使用 SCSS 进行样式编写。

BEM 是一种前端开发方法论,全称是 Block Element Modifier(块、元素、修饰符)。它提供了一种命名约定,用于组织和管理 CSS 类名,从而提高代码的可维护性、可扩展性和复用性。

  • Block(块)

    • 独立的功能模块,可以独立存在
    • 示例:buttonmenuinput
  • Element(元素)

    • 属于某个 Block 的一部分,不能单独存在
    • 使用双下划线 __ 连接 Block 和 Element
    • 示例:menu__itembutton__text
  • Modifier(修饰符)

    • 用于改变 Block 或 Element 的外观或行为
    • 使用双横线 -- 表示
    • 示例:button--largemenu__item--active

BEM 方法的引入主要是为了解决传统 CSS 开发中常见的问题,尤其是在大型项目或团队协作中,这些问题会变得更加突出。以下是使用 BEM 的主要原因以及它解决的痛点:

  • 样式冲突:在传统的 CSS 开发中,类名可能会重复或不够具体,导致样式冲突。例如,多个开发者可能都定义了一个名为 button 的样式,但它们的行为和外观完全不同
  • 可维护性差:随着项目的增长,CSS 文件变得越来越复杂,难以找到特定样式的定义位置,或者修改一个样式时意外影响到其他部分
  • 样式复用困难:在没有明确规范的情况下,开发者可能需要重复编写类似的样式代码,增加了冗余
  • 团队协作困难:在多人协作的项目中,不同开发者可能采用不同的命名习惯,导致代码风格不一致,难以统一管理
  • 样式与结构分离不清晰:在某些情况下,开发者可能直接通过 HTML 结构(如标签选择器、后代选择器)来定义样式,这会导致样式与结构紧密耦合,难以迁移或重构
  • 缺乏扩展性:当需要对现有样式进行扩展或修改时,可能会因为复杂的嵌套关系或不清晰的命名规则而感到困难

BEM 命名规则

  • Block: blockName
  • Block + Element: blockName__elementName
  • Block + Modifier: blockName--modifierName
  • Element + Modifier: blockName__elementName--modifierName
html
<div class="button">
  <span class="button__text">文字按钮</span>
  <button class="button--large">large 按钮</button>
  <span class="button__text--bold">文字加粗按钮</span>
</div>
css
/* Block */
.button {
  /* 样式 */
}

/* Element */
.button__text {
  /* 样式 */
}

/* Modifier */
.button--large {
  /* 样式 */
}

/* Element + Modifier */
.button__text--bold {
  /* 样式 */
}

组件元素使用 BEM

Teek 已经定义了 useNamespace 函数,用于生成 BEM 样式。

vue
<script setup lang="ts" name="BEMDemo">
import { useNamespace } from "@/composables";

const ns = useNamespace("button");
</script>

<template>
  <div :class="ns.b()">
    <span :class="ns.e('text')">文字按钮</span>
    <button :class="ns.m('large')">large 按钮</button>
    <span :class="ns.em('text', 'bold')">文字加粗按钮</span>
    <button :class="['button', ns.is('primary')]">primary 按钮</button>
  </div>
</template>

<style scoped lang="scss">
@use "../mixins/bem" as *;

@include b("button") {
  @include e("text") {
    @include m("bold") {
    }
  }

  @include m("large") {
  }

  .button {
    @include when("primary") {
    }
  }
}
</style>

等于:

vue
<script setup lang="ts" name="BEMDemo"></script>

<template>
  <div class="tk-button">
    <span class="tk-button__text">文字按钮</span>
    <button class="tk-button--large">large 按钮</button>
    <span class="tk-button__text--bold">文字加粗按钮</span>
    <button class="button is-primary">primary 按钮</button>
  </div>
</template>

<style scoped lang="scss">
.tk-button {
  .tk-button__text {
    &--bold {
    }
  }

  .tk-button--large {
  }

  .button {
    &.is-primary {
    }
  }
}
</style>

CSS Var 变量使用命名空间

Teek 给所有内置的 CSS Var 变量都添加命名空间,如:

scss
@use "@/common/style/mixins/namespace" as *;

:root {
  --#{$teek-namespace}-primary: #395ae3;
}

当使用变量时,可以通过 cssVar 函数获取变量值:

scss
@use "@/common/style/mixins/function" as *;

.demo {
  width: cssVar(primary);
}

cssVar 函数内部自动添加命名空间。