element-plus源码解读1——useNamespace
2025年11月29日 15:50
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