阅读视图

发现新文章,点击刷新页面。

el-button源码解读3——:class="buttonKls"与颜色系统的关系

说明 :class="buttonKls" 与颜色系统的关系:

1. buttonKls 的作用

buttonKls 是一个计算属性,生成按钮需要的所有 CSS 类名:

const buttonKls = computed(() => [
  // el-button
  ns.b(),
  ns.m(_type.value),
  ns.m(_size.value),
  ns.is('disabled', _disabled.value),
  ns.is('loading', props.loading),
  ns.is('plain', _plain.value),
  ns.is('round', _round.value),
  ns.is('circle', props.circle),
  ns.is('text', _text.value),
  ns.is('link', props.link),
  ns.is('has-bg', props.bg),
])

假设 type="primary"buttonKls 可能包含:

['el-button', 'el-button--primary', 'el-button--small', 'is-loading', ...]

2. 颜色系统的触发机制

颜色系统通过 CSS 类选择器触发。当 buttonKls 包含 'el-button--primary' 时,会匹配到对应的 CSS 规则。

3. 完整的关系链

用户使用: <el-button type="primary">
    ↓
props.type = 'primary'_type.value = 'primary'buttonKls = ['el-button', 'el-button--primary', ...]
    ↓
:class="buttonKls"  →  class="el-button el-button--primary"
    ↓
CSS 匹配: .el-button--primary { ... }
    ↓
button-variant('primary') 生成 CSS 变量
    ↓
应用颜色样式

4. 在 CSS 中的对应关系

button.scss 中的定义:

  @each $type in (primary, success, warning, danger, info) {
    @include m($type) {
      @include button-variant($type);
    }
  }

$type = 'primary' 时:

  • @include m('primary') → 生成 .el-button--primary 选择器
  • @include button-variant('primary') → 生成颜色相关的 CSS 变量

5. 实际渲染过程

步骤 1:Vue 组件生成类名

<el-button type="primary">按钮</el-button>
// buttonKls 计算后
['el-button', 'el-button--primary']
<!-- 最终渲染 -->
<button class="el-button el-button--primary">
  按钮
</button>

步骤 2:CSS 匹配类名

浏览器看到 class="el-button--primary",匹配到:

// button.scss 中生成的
.el-button--primary {
  // button-variant('primary') 生成的 CSS 变量
  --el-button-bg-color: var(--el-color-primary);
  --el-button-border-color: var(--el-color-primary);
  --el-button-text-color: var(--el-color-white);
  --el-button-hover-bg-color: var(--el-color-primary-light-3);
  --el-button-active-bg-color: var(--el-color-primary-dark-2);
}

步骤 3:应用颜色

基础样式使用这些 CSS 变量:

.el-button {
  background-color: var(--el-button-bg-color);
  border-color: var(--el-button-border-color);
  color: var(--el-button-text-color);
}

因为 .el-button--primary 定义了这些变量,所以按钮会显示 primary 的颜色。

6. 关键理解

:class="buttonKls" 是连接 Vue 组件和 CSS 颜色系统的桥梁:

Vue 组件层面          CSS 样式层面
─────────────────────────────────
buttonKls            .el-button--primary
  ↓                       ↓
'el-button--primary'  →  匹配 CSS 选择器
  ↓                       ↓
应用类名              应用颜色样式

7. 不同类型的关系

用户传入 buttonKls 包含 CSS 匹配 颜色应用
type="primary" 'el-button--primary' .el-button--primary 蓝色 (#409eff)
type="success" 'el-button--success' .el-button--success 绿色 (#67c23a)
type="warning" 'el-button--warning' .el-button--warning 橙色 (#e6a23c)
type="danger" 'el-button--danger' .el-button--danger 红色 (#f56c6c)

8. 完整示例

<el-button type="primary" size="small" :loading="true">
  提交
</el-button>

生成的类名:

buttonKls = [
  'el-button',           // 基础类
  'el-button--primary',  // 类型类(触发颜色系统)
  'el-button--small',    // 尺寸类
  'is-loading'           // 状态类
]

最终 HTML:

<button class="el-button el-button--primary el-button--small is-loading">
  提交
</button>

CSS 匹配:

.el-button--primary {  // ← 这个类触发了颜色系统
  --el-button-bg-color: var(--el-color-primary);
  // ...
}

9. 总结

  • :class="buttonKls" 生成类名(如 el-button--primary
  • CSS 通过类选择器匹配这些类名
  • 匹配到的规则通过 button-variant 生成颜色变量
  • 基础样式使用这些变量,最终显示对应颜色

核心关系buttonKls 中的类型类名(如 el-button--primary)是触发颜色系统的开关,CSS 通过这个类名应用对应的颜色样式。

element-plus源码解读1——useNamespace

useNamespace

useNamespace位于packages/hooks/use-namespace, 旨在帮所有组件统一生成类名/变量名,遵循BEM规范

什么是BEM规范?可阅读下面这篇文章blog.csdn.net/fageaaa/art…

element-plus的BEM类名生成函数_bem

const _bem = (
  namespace: string, // 命名空间,通常是el
  block: string, // 块名,例如button
  blockSuffix: string, // 块后缀(可选),用于块的变体
  element: string, // 元素(可选),用__连接
  modifier: string // 修饰符(可选),用--连接
) => {
  let cls = `${namespace}-${block}`
  if (blockSuffix) {
    cls += `-${blockSuffix}`
  }
  if (element) {
    cls += `__${element}`
  }
  if (modifier) {
    cls += `--${modifier}`
  }
  return cls
}

### 1. 参数说明

-   namespace:命名空间,通常是 'el'
-   block:块名,如 'button'
-   blockSuffix:块后缀(可选),用于块的变体
-   element:元素(可选),用 __ 连接
-   modifier:修饰符(可选),用 -- 连接

### 2. 生成规则(按顺序拼接)

-   基础:namespace-block → 'el-button'
-   如果有 blockSuffix:追加 -${blockSuffix} → 'el-button-suffix'
-   如果有 element:追加 __${element} → 'el-button__icon'
-   如果有 modifier:追加 --${modifier} → 'el-button--primary'

el-button组件为例子

const ns = useNamespace('button')

ns.namespace.value  // → 'el'
  • b-Block(块)
const b = (blockSuffix = '') => _bem(namespace.value, block, blockSuffix, '', '')

ns.b()  // el-button
ns.b('group')  // el-button-group
  • e-Element(元素)
const e = (element?: string) => element ? _bem(namespace.value, block, '', element, '') : ''

ns.e('icon')  // el-button__icon
ns.e('text')  // el-button__text
ns.e()  // 返回一个空字符串'', 因为传入的element:string参数是空
  • e-Modifier(修饰符)
const m = (modifier?: string) => modifier ? _bem(namespace.value, block, '', '', modifier) : ''

ns.m('primary')  // el-button--primary
ns.m('small')  // el-button--small
ns.m('disabled')  // el-button--disabled
ns.m()  // '' (空字符串)
  • be-Block+Element (块后缀+元素)
  const be = (blockSuffix?: string, element?: string) =>
    blockSuffix && element
      ? _bem(namespace.value, block, blockSuffix, element, '')
      : ''

ns.be('group', 'item') // el-button-group__item
ns.be('group', '') // ''
ns.be('', 'group')  // ''
  • em-Element+Modifier (元素+修饰符)
  const em = (element?: string, modifier?: string) =>
    element && modifier
      ? _bem(namespace.value, block, '', element, modifier)
      : ''
      
ns.em('icon', 'loading') // el-button__icon--loading
ns.em('text', 'expand') // el-button__text--expand
ns.em('icon', '') // ''
ns.em('', 'loading') // ''
  • bm-Block+Modifier (块后缀+修饰符)
  const bm = (blockSuffix?: string, modifier?: string) =>
    blockSuffix && modifier
      ? _bem(namespace.value, block, blockSuffix, '', modifier)
      : ''
      
ns.bm('group', 'vertical') // el-button-group--vertical
ns.bm('group', '') // ''
ns.bm('', 'primary') // ''
  • bem-Block+Element+Modifier (块后缀+元素+修饰符)
  const bem = (blockSuffix?: string, element?: string, modifier?: string) =>
    blockSuffix && element && modifier
      ? _bem(namespace.value, block, blockSuffix, element, modifier)
      : ''
      
ns.bem('group', 'item', 'active') // el-button-group__item--active
ns.bem('group', 'item', '') // '' 必须三个参数都有值
  • is-State 状态类
  const statePrefix = 'is-'

  const is: {
    (name: string, state: boolean | undefined): string
    (name: string): string
  } = (name: string, ...args: [boolean | undefined] | []) => {
    const state = args.length >= 1 ? args[0]! : true // args[0]! ts的非空断言
    return name && state ? `${statePrefix}${name}` : ''
  }
  
ns.is('loading')  // is-loading
ns.is('loading', true)  // is-loading
ns.is('loading', false)  // ''
ns.is('disabled', true)  // is-disabled
ns.is('disabled', undefined) // ''
  • cssVar-CSS变量(全局命名空间)
  const cssVar = (object: Record<string, string>) => {
    const styles: Record<string, string> = {}
    for (const key in object) {
      if (object[key]) {
        styles[`--${namespace.value}-${key}`] = object[key]
      }
    }
    return styles
  }
  
  ns.cssVar({ color: 'red', size: '10px'}) // {'--el-color': 'red', '--el-size': '10px'}
  • cssVarName-CSS 变量名(全局)
const cssVarName = (name: string) => `--${namespace.value}-${name}`

ns.cssVarName('color')  // → '--el-color'
ns.cssVarName('size')   // → '--el-size'

补充:命名空间与变量名的区别 命名空间:用{}包裹起来的批量的CSS变量+赋值,可以直接绑定到元素的style属性上 变量名:仅仅是一个单独的没有被赋值的变量,需要自己使用

cssVar 的使用场景(批量设置变量值)

<template>
  <div :style="customStyles">
    <!-- 这个 div 会应用这些 CSS 变量 -->
  </div>
</template>

<script setup>
const ns = useNamespace('button')
const customStyles = ns.cssVar({
  color: 'blue',
  fontSize: '16px'
})
// customStyles = { '--el-color': 'blue', '--el-fontSize': '16px' }
</script>

cssVarName 的使用场景(引用已存在的变量)

<template>
  <div :style="{ color: `var(${colorVarName})` }">
    <!-- 使用 cssVarName 获取变量名,然后用 var() 引用 -->
  </div>
</template>

<script setup>
const ns = useNamespace('button')
const colorVarName = ns.cssVarName('color')
// colorVarName = '--el-color'

// 然后在 CSS 或 style 中使用:
// color: var(--el-color)
</script>
  • cssVarBlock-CSS变量(带block)
  const cssVarBlock = (object: Record<string, string>) => {
    const styles: Record<string, string> = {}
    for (const key in object) {
      if (object[key]) {
        styles[`--${namespace.value}-${block}-${key}`] = object[key]
      }
    }
    return styles
  }
  
============
// 步骤 1: 创建命名空间实例,传入 'button' 作为 block
const ns = useNamespace('button')
// 此时 ns 内部保存了 block = 'button'

// 步骤 2: 调用 cssVarBlock
ns.cssVarBlock({ color: 'blue', fontSize: '14px' })

// 步骤 3: cssVarBlock 内部使用闭包中的 block
// 生成:'--el-button-color': 'blue'
// 生成:'--el-button-fontSize': '14px'
===========
ns.cssVarBlock({ color: 'blue', fontSize: '14px' })
// → { '--el-button-color': 'blue', '--el-button-fontSize': '14px' }
  • cssVarBlockName-CSS变量名(带block)
  const cssVarBlockName = (name: string) =>
    `--${namespace.value}-${block}-${name}`

ns.cssVarBlockName('color') // --el-button-color
ns.cssVarBlockName('bgColor') // --el-button-bgColor
❌