样式
项目结构
项目中的样式文件存放在 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(块)
- 独立的功能模块,可以独立存在
- 示例:
button
、menu
、input
Element(元素)
- 属于某个 Block 的一部分,不能单独存在
- 使用双下划线
__
连接 Block 和 Element - 示例:
menu__item
、button__text
Modifier(修饰符)
- 用于改变 Block 或 Element 的外观或行为
- 使用双横线
--
表示 - 示例:
button--large
、menu__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
函数内部自动添加命名空间。