这是setup中引用的所有模块,现在一一开始拆解
import { computed, inject, Text, ref, useSlots } from 'vue'
import { TinyColor } from '@ctrl/tinycolor'
import { ElIcon } from '@element-plus/components/icon'
import {
useDisabled,
useFormItem,
useGlobalConfig,
useNamespace,
useSize,
} from '@element-plus/hooks'
import { buttonGroupContextKey } from '@element-plus/tokens'
import { buttonEmits, buttonProps } from './button'
1.buttonGroupContextKey-很明显针对groupButton的操作,详情在groupButton组件中,这里不展开讲
defineOptions({
name: 'ElButton',
})
const props = defineProps(buttonProps)
const emit = defineEmits(buttonEmits)
const slots = useSlots()
const buttonGroupContext = inject(buttonGroupContextKey, undefined)
2*.useGlobalConfig - 全局配置hooks -可以发现目的是取出provide提供ConfigProviderContext 为key的值,这里使用此方法是为了适配config-provider组件(不展开),简单的说就是button外层有提供config-provider即有值,未提供则无值
const globalConfig = useGlobalConfig('button')
import { inject, ref, computed, unref, provide, getCurrentInstance } from 'vue'
import { configProviderContextKey } from '@element-plus/tokens'
import { debugWarn, keysOf } from '@element-plus/utils'
import type { MaybeRef } from '@vueuse/core'
import type { Ref, App } from 'vue'
import type { ConfigProviderContext } from '@element-plus/tokens'
// this is meant to fix global methods like `ElMessage(opts)`, this way we can inject current locale
// into the component as default injection value.
// refer to: https://github.com/element-plus/element-plus/issues/2610#issuecomment-887965266
const globalConfig = ref<ConfigProviderContext>()
export function useGlobalConfig<
K extends keyof ConfigProviderContext,
D extends ConfigProviderContext[K]
>(
key: K,
defaultValue?: D
): Ref<Exclude<ConfigProviderContext[K], undefined> | D>
export function useGlobalConfig(): Ref<ConfigProviderContext>
export function useGlobalConfig(
key?: keyof ConfigProviderContext,
defaultValue = undefined
) {
const config = getCurrentInstance()
? inject(configProviderContextKey, globalConfig)
: globalConfig
if (key) {
return computed(() => config.value?.[key] ?? defaultValue)
} else {
return config
}
}
export const provideGlobalConfig = (
config: MaybeRef<ConfigProviderContext>,
app?: App,
global = false
) => {
const inSetup = !!getCurrentInstance()
const oldConfig = inSetup ? useGlobalConfig() : undefined
const provideFn = app?.provide ?? (inSetup ? provide : undefined)
if (!provideFn) {
debugWarn(
'provideGlobalConfig',
'provideGlobalConfig() can only be used inside setup().'
)
return
}
const context = computed(() => {
const cfg = unref(config)
if (!oldConfig?.value) return cfg
return mergeConfig(oldConfig.value, cfg)
})
provideFn(configProviderContextKey, context)
if (global || !globalConfig.value) {
globalConfig.value = context.value
}
return context
}
const mergeConfig = (
a: ConfigProviderContext,
b: ConfigProviderContext
): ConfigProviderContext => {
const keys = [...new Set([...keysOf(a), ...keysOf(b)])]
const obj = {}
for (const key of keys) {
obj[key] = b[key] ?? a[key]
}
return obj
}
3*.useNamespace -- 可以看出此hook主要是针对样式进行处理
const ns = useNamespace('button')
<template>
<button
ref="_ref"
:class="[
ns.b(),
ns.m(_type),
ns.m(_size),
ns.is('disabled', _disabled),
ns.is('loading', loading),
ns.is('plain', plain),
ns.is('round', round),
ns.is('circle', circle),
]"
:disabled="_disabled || loading"
:autofocus="autofocus"
:type="nativeType"
:style="buttonStyle"
@click="handleClick"
>
<template v-if="loading">
<slot v-if="$slots.loading" name="loading"></slot>
<el-icon v-else :class="ns.is('loading')">
<component :is="loadingIcon" />
</el-icon>
</template>
<el-icon v-else-if="icon || $slots.icon">
<component :is="icon" v-if="icon" />
<slot v-else name="icon" />
</el-icon>
<span
v-if="$slots.default"
:class="{ [ns.em('text', 'expand')]: shouldAddSpace }"
>
<slot></slot>
</span>
</button>
</template>
在这里 block=button,namespace要么从useGlobalConfig取出要么取默认-这里为el,然后根据传入的属性不同生成不一样的className
_bem(unref(namespace), block, blockSuffix, element, modifier)
那么生成的结果即为 ${namespace}-${block}-${blockSuffix}?__${element}?--${modifier}?
例如 ns.b()
const b = (blockSuffix = '') => _bem(unref(namespace), block, blockSuffix, '', '')
b = el-button
如果定制样式的需求,即可使用 ns.b('my') 生成的class为el-button-my
其他的同理 那么 ns.m(_type) 也就知道是什么了吧 根据使用者传的type来决定
'default',
'primary',
'success',
'warning',
'info',
'danger',
'text',
'',
const _type = computed(() => props.type || buttonGroupContext?.type || '')
import { unref, computed } from 'vue'
import { useGlobalConfig } from '../use-global-config'
const defaultNamespace = 'el'
const statePrefix = 'is-'
const _bem = (
namespace: string,
block: string,
blockSuffix: string,
element: string,
modifier: string
) => {
let cls = `${namespace}-${block}`
if (blockSuffix) {
cls += `-${blockSuffix}`
}
if (element) {
cls += `__${element}`
}
if (modifier) {
cls += `--${modifier}`
}
return cls
}
export const useNamespace = (block: string) => {
const globalConfig = useGlobalConfig('namespace')
const namespace = computed(() => globalConfig.value || defaultNamespace)
const b = (blockSuffix = '') =>
_bem(unref(namespace), block, blockSuffix, '', '')
const e = (element?: string) =>
element ? _bem(unref(namespace), block, '', element, '') : ''
const m = (modifier?: string) =>
modifier ? _bem(unref(namespace), block, '', '', modifier) : ''
const be = (blockSuffix?: string, element?: string) =>
blockSuffix && element
? _bem(unref(namespace), block, blockSuffix, element, '')
: ''
const em = (element?: string, modifier?: string) =>
element && modifier
? _bem(unref(namespace), block, '', element, modifier)
: ''
const bm = (blockSuffix?: string, modifier?: string) =>
blockSuffix && modifier
? _bem(unref(namespace), block, blockSuffix, '', modifier)
: ''
const bem = (blockSuffix?: string, element?: string, modifier?: string) =>
blockSuffix && element && modifier
? _bem(unref(namespace), block, blockSuffix, element, modifier)
: ''
const is: {
(name: string, state: boolean | undefined): string
(name: string): string
} = (name: string, ...args: [boolean | undefined] | []) => {
const state = args.length >= 1 ? args[0]! : true
return name && state ? `${statePrefix}${name}` : ''
}
return {
namespace,
b,
e,
m,
be,
em,
bm,
bem,
is,
}
}