返回首页

Vue 中的 BEM:组件样式

本文详解 Vue 中 BEM 用于结构化样式。按钮修饰符示例、scoped 和扩展规则优势。适合中高级开发者。

BEM + Vue:可靠的样式系统
Advertisement 728x90

Vue 中使用 BEM:构建可扩展组件的样式结构

BEM 在 Vue 项目中依然大放异彩,得益于其严格的类名层级,能有效避免组件化架构中的样式冲突。Vue 负责响应式和逻辑处理,而 BEM 确保块(Block)和元素(Element)保持独立。这种组合能有效防止应用规模扩大时出现 CSS 杂乱无章的情况。

BEM 基于三大核心原则:

  • 块(Block):独立实体,如 .header.card,无外部依赖。
  • 元素(Element):块的子部分,用双下划线表示 — .button__icon
  • 修饰符(Modifier):变体或状态,用单下划线表示 — .button_disabled.card_theme_dark

这种结构隔离了样式,让调试和重构变得轻松自如。

Google AdInline article slot

Vue 组件中的样式编写

在 Vue 中,样式放在 <style scoped> 内,限制作用域仅限于当前组件。Scoped 能防止样式泄漏,但要小心深层嵌套。

仅在集成外部库且 scoped 不够用时,才使用 :deep()。别用它来绕过自己的架构设计 — 那往往是结构问题的信号。

BEM 与 scoped 完美搭配,通过逻辑分组类名。块定义基础样式,修饰符处理变体,元素管理内部结构。

Google AdInline article slot

实战示例:带状态的按钮

没有体系,类名会乱七八糟地堆积。这是典型问题:

<template>
  <button :class="{
    'button-basic': isBasic,
    'button-href': isHref,
    'button-avatar': isAvatar 
  }">
    {{ text }}
  </button>
</template>

问题:

  • 无层级:类名未绑定到块。
  • 扩展性差:新状态意味着更多条件。
  • 可读性低:逻辑随时间变得晦涩。

使用 BEM,模板简化且自文档化:

Google AdInline article slot
<template>
  <button :class="[
    'button',
    {
      'button_disabled': disabled,
      'button_loading': loading,
    },
    {
      'button_size_xl': size === 'xl',
      'button_size_l': size === 'l',
    },
    {
      'button_primary': color === 'primary',
      'button_secondary': color === 'secondary',
    }
  ]">
    {{ text }}
  </button>
</template>

类名按语义分组:基础块、状态、大小、主题。在 CSS 中:

.button {
  /* 基础样式 */
  padding: 12px 24px;
  border: none;
  border-radius: 4px;
}

.button_disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.button_loading {
  position: relative;
}

.button_primary:not(.button_disabled):hover {
  background-color: #0055ff;
}

:not() 选择器能过滤冲突状态,无需额外类名。

扩展与团队协作

在大项目中,BEM 降低认知负担:开发者一看模板,就能明白块及其修饰符,无需翻 CSS 文件。

团队收益:

  • 可预测性:类名直观反映用途。
  • 隔离性:块间互不干扰。
  • 可扩展性:新增修饰符无需重构。

复杂组件示例 — 带主题和尺寸的产品卡片:

<template>
  <div class="card" :class="[
    `card_size_${size}`,
    `card_theme_${theme}`
  ]">
    <div class="card__image"></div>
    <div class="card__title">{{ title }}</div>
    <button class="card__button button">购买</button>
  </div>
</template>

块内元素继承上下文但保持独立。

何时跳过 BEM

原型或落地页用 BEM 有点小题大做:Tailwind 或工具类更快。长期项目、团队协作和复杂 UI 才适合 BEM。

核心要点

  • BEM 以严格层级补充 Vue,与 scoped 样式零冲突。
  • 修饰符归类状态和变体,简化模板。
  • :deep() 仅限外部库,别用于内部 hack。
  • 大项目中,BEM 加速新人上手和重构。
  • 试试真实组件:可读性立竿见影。

— Editorial Team

Advertisement 728x90

继续阅读