大家好,我是wo不是黄蓉,今年学习目标从源码共读开始,希望能跟着若川大佬学习源码的思路学到更多的东西。
packages\hooks\use-namespace\index.ts
namespace
命名空间,block
块,blockSuffix
块前缀 ,element
元素,modifier
块或元素的一个标识,用它来描述外表或者行为。
如果是块元素,生成规则如:el-form ,el-form-item
如果是元素:生成规则如:el-tag__content ,el-tooltip__trigger
如果是标识:生成规则如:el-button--primary,el-tag--small
import { useGlobalConfig } from '../use-global-config'
//默认命名空间
export const defaultNamespace = 'el'
//状态前缀
const statePrefix = 'is-'
//bem命名规范
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 namespace = useGlobalConfig('namespace', defaultNamespace)
const b = (blockSuffix = '') =>
_bem(namespace.value, block, blockSuffix, '', '')
const e = (element?: string) =>
element ? _bem(namespace.value, block, '', element, '') : ''
const m = (modifier?: string) =>
modifier ? _bem(namespace.value, block, '', '', modifier) : ''
const be = (blockSuffix?: string, element?: string) =>
blockSuffix && element
? _bem(namespace.value, block, blockSuffix, element, '')
: ''
const em = (element?: string, modifier?: string) =>
element && modifier
? _bem(namespace.value, block, '', element, modifier)
: ''
const bm = (blockSuffix?: string, modifier?: string) =>
blockSuffix && modifier
? _bem(namespace.value, block, blockSuffix, '', modifier)
: ''
const bem = (blockSuffix?: string, element?: string, modifier?: string) =>
blockSuffix && element && modifier
? _bem(namespace.value, 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}` : ''
}
// for css var
// --el-xxx: value;
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
}
// with 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
}
const cssVarName = (name: string) => `--${namespace.value}-${name}`
const cssVarBlockName = (name: string) =>
`--${namespace.value}-${block}-${name}`
return {
namespace,
b,
e,
m,
be,
em,
bm,
bem,
is,
// css
cssVar,
cssVarName,
cssVarBlock,
cssVarBlockName,
}
}
export type UseNamespaceReturn = ReturnType<typeof useNamespace>
packages\hooks\use-global-config\index.ts
getCurrentInstance可以用来获取当前组件实例
这边不知道为什么会到provider?
import { configProviderContextKey } from '@element-plus/tokens'
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
}
}
packages\tokens\config-provider.ts
import type { ConfigProviderProps } from '@element-plus/components/config-provider'
import type { InjectionKey, Ref } from 'vue'
export type ConfigProviderContext = Partial<ConfigProviderProps>
export const configProviderContextKey: InjectionKey<
Ref<ConfigProviderContext>
> = Symbol()
packages\components\config-provider\src\config-provider.ts
element-plus
提供的一些provider
,我理解的就是一个提供者,为我们使用某些特殊配置提供方便,通常配合inject
使用
a11y
不知道是个啥,普及了一下大概意思就是提高页面可访问性的一些方法,参考
locale
本地化的一些配置,默认比如我们引入默认是英文的,使用该配置改成中文环境
size
组件大小配置
button
按钮配置
experimentalFeatures
实验性的特性,我看的版本0.0.0-dev.1
暂时没啥实验性的特性
keyboardNavigation
键盘导航,这个不知道干啥的
message
消息配置
zIndex
初始化遮罩层,之前我们需要使用重写样式来解决
`namespace
自定义命名空间
使用element-plus provider
的例子:
<script setup>
import HelloWorld from "./components/HelloWorld.vue"
//局部引入
import { ElConfigProvider } from "element-plus"
import zhCn from "element-plus/lib/locale/lang/zh-cn"
import { reactive } from "vue"
const { locale } = reactive({
locale: zhCn,
})
</script>
<template>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src="/vite.svg" class="logo" alt="Vite logo" />
</a>
<a href="https://vuejs.org/" target="_blank">
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
</a>
</div>
<!--默认是英文环境-->
<el-config-provider>
<HelloWorld msg="Vite + Vue" />
</el-config-provider>
<!--使用provider-->
<el-config-provider :locale="locale">
<HelloWorld msg="Vite + Vue" />
</el-config-provider>
</template>
使用命名空间的例子:组件上层包裹一层provider
和react
很像
<el-config-provider :locale="locale" :size="size" namespace="bl-">
<HelloWorld msg="Vite + Vue" />
</el-config-provider>
HelloWOrld.vue
<bl-button type="danger">bl button</bl-button>
因为样式库都是用的el-
开头的所以会出现样式丢失,解决:在自己的样式文件上加这两句话,main.js
中引入自己的样式文件即可,可以发现使用自定义的命名空间和原生的不太一样,原生的将el-button
解析为button
,自定义的将bl-button
还是解析为bl-button
,这个咋实现的?后续接着聊
@forward "element-plus/theme-chalk/src/mixins/config.scss" with (
$namespace: "bl" // 自己需要的前缀
);
@import "element-plus/theme-chalk/src/index.scss";
export const configProviderProps = buildProps({
a11y: {
type: Boolean,
default: true,
},
locale: {
type: definePropType<Language>(Object),
},
size: useSizeProp,
button: {
type: definePropType<ButtonConfigContext>(Object),
},
experimentalFeatures: {
type: definePropType<ExperimentalFeatures>(Object),
},
keyboardNavigation: {
type: Boolean,
default: true,
},
message: {
type: definePropType<MessageConfigContext>(Object),
},
zIndex: Number,
namespace: {
type: String,
default: 'el',
},
} as const)
export type ConfigProviderProps = ExtractPropTypes<typeof configProviderProps>
packages\element-plus\index.ts
在这边导出hooks
然后就可以进行使用了packages\components\button\src\button.vue
<script lang="ts" setup>
import { useNamespace } from '@element-plus/hooks'
const ns = useNamespace('button')
</script>
<template>
<button
ref="_ref"
:class="[
ns.b(),
ns.m(_type),
ns.is('disabled', _disabled),
]"
@click="handleClick"
>
<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 />
</span>
</button>
</template>
接下来就是使用BEM规范
创建样式代码了,BEM参考
下节接着研究咋使用BEM
规范样式
上面例子是用vite
跑起来的一个很简单的例子,参考文档安装element
引入element
即可进行实战。