|
| 1 | +--- |
| 2 | +outline: deep |
| 3 | +layout: doc |
| 4 | +--- |
| 5 | + |
| 6 | +ui组件库的特点 |
| 7 | +* 可重用性,提高开发效率 |
| 8 | +* 一致性,设计风格一致 |
| 9 | +* 易用性,组件经过设计和测试,具有良好的用户体验 |
| 10 | +* 定制性,允许开发人员按照自己的需求和设计风格进行定制 |
| 11 | + |
| 12 | +## 目录结构 |
| 13 | +1. Element Plus 的根目录 |
| 14 | + |
| 15 | +.circleci:用于 Circle CI 自动化部署的配置文件。 |
| 16 | +.github:存放 GitHub 的配置文件,如 issue、PR 模板等。 |
| 17 | +.husky:用于 Git 钩子的配置。 |
| 18 | +.vscode:为 VSCode 提供的配置文件。 |
| 19 | +docs:使用 VitePress 生成的文档配置。 |
| 20 | +internal:内部工具和脚本。 |
| 21 | +play:测试项目。 |
| 22 | +scripts:构建脚本。 |
| 23 | +typings:类型定义文件。 |
| 24 | +packages:核心目录,包含所有功能包 。 |
| 25 | + |
| 26 | +2. packages 目录结构 |
| 27 | +packages 是 Element Plus 的核心目录,包含多个功能包,每个包都具有清晰的结构。以下是主要包的结构和功能: |
| 28 | + |
| 29 | +2.1 components |
| 30 | +功能:存放所有 UI 组件的源码。 |
| 31 | +2.2 utils |
| 32 | +2.3 theme-chalk |
| 33 | +提供组件的样式文件(SCSS)。 |
| 34 | +2.4 element-plus |
| 35 | +对外暴露的包,是组件库的统一出口。导出所有组件和功能。 |
| 36 | +index.ts:入口文件,用于集中注册组件。 |
| 37 | +2.5 hooks |
| 38 | +存放自定义 hook 函数。 |
| 39 | +2.7 directives |
| 40 | +2.8 test-utils |
| 41 | +功能:测试工具和辅助函数。 |
| 42 | +2.9 constants |
| 43 | +功能:存放公用常量。 |
| 44 | + |
| 45 | +3.组件 目录结构 |
| 46 | +3.1 src包含.vue,style文件 |
| 47 | +3.2 index.ts 访问组件的入口文件 |
| 48 | +## 使用monorepo构建 |
| 49 | +适用于大型项目,和具有多个相互依赖部分的application |
| 50 | + |
| 51 | +搭建好目录之后,他们都变成了独立的包.要使他们可以相互调用,需要在根目录下按照依赖即可 |
| 52 | + |
| 53 | +例如 `pnpm i @template/components@workspace:* -w` |
| 54 | + |
| 55 | +## 如何实现完整引入 |
| 56 | +```js |
| 57 | +import ElementPlus from 'element-plus' |
| 58 | + |
| 59 | +const app = createApp(App) |
| 60 | +app.use(ElementPlus) |
| 61 | +``` |
| 62 | +packages下的element-plus是组件库的统一出口 |
| 63 | + |
| 64 | +ElementPlus是一个插件,他实际上是一个包含install的对象 |
| 65 | + |
| 66 | +他是这么生成的 |
| 67 | + |
| 68 | +```js |
| 69 | +//packages/element-plus |
| 70 | +makeInstaller([...Components]) |
| 71 | +export const makeInstaller = (components: Plugin[] = []) => { |
| 72 | + const install = (app: App, options?: ConfigProviderContext) => {//这里实现了第一个install方法 |
| 73 | + if (app[INSTALLED_KEY]) return |
| 74 | + |
| 75 | + app[INSTALLED_KEY] = true |
| 76 | + components.forEach((c) => app.use(c))//这里会用到下面注入的install |
| 77 | + |
| 78 | + if (options) provideGlobalConfig(options, app, true) |
| 79 | + } |
| 80 | + |
| 81 | + return { |
| 82 | + version, |
| 83 | + install, |
| 84 | + } |
| 85 | +} |
| 86 | + |
| 87 | +//packages/components/* |
| 88 | +//对于每个组件,导出的时候使用withInstall方法处理一下,作用是注入一个install方法,返回这个组件而已 |
| 89 | +export const ElAffix: SFCWithInstall<typeof Affix> = withInstall(Affix) |
| 90 | +export const withInstall = <T, E extends Record<string, any>>( |
| 91 | + main: T, |
| 92 | + extra?: E |
| 93 | +) => { |
| 94 | + ;(main as SFCWithInstall<T>).install = (app): void => { |
| 95 | + for (const comp of [main, ...Object.values(extra ?? {})]) { |
| 96 | + app.component(comp.name, comp) |
| 97 | + } |
| 98 | + } |
| 99 | + |
| 100 | + if (extra) { |
| 101 | + for (const [key, comp] of Object.entries(extra)) { |
| 102 | + ;(main as any)[key] = comp |
| 103 | + } |
| 104 | + } |
| 105 | + return main as SFCWithInstall<T> & E |
| 106 | +} |
| 107 | +``` |
| 108 | +## 实现按需手动导入 |
| 109 | +因为每个组件都被导出了,所有只需要`export * from '@element-plus/components'`即可,就可以被引用了 |
| 110 | +
|
| 111 | +样式>>>>>>>>>wait |
| 112 | +
|
| 113 | +## 实现按需自动加载 |
| 114 | +只需要添加两个插件`unplugin-vue-components 和 unplugin-auto-import` |
| 115 | +一个负责自动扫描和注册vue组件,包括流行ui库以及src/components的所有组件 |
| 116 | +
|
| 117 | +另一个负责导入流行库的api |
| 118 | +
|
| 119 | +## css |
| 120 | +使用的是BEM命名规则,组件用-连接,元素用__连接,类型用--连接,状态用is-xx表示 |
| 121 | +
|
| 122 | +编写组件时,如果都是手写类名,写法繁琐,可以把BEM命名规则封装风函数,动态生成类名 |
| 123 | +
|
| 124 | +```js |
| 125 | +export const defaultNamespace = 'el' |
| 126 | +const statePrefix = 'is-' |
| 127 | + |
| 128 | +export const useGetDerivedNamespace = (//这个函数可以更改命名空间方便二次开发 |
| 129 | + namespaceOverrides?: Ref<string | undefined> |
| 130 | +) => { |
| 131 | + const derivedNamespace = |
| 132 | + namespaceOverrides || |
| 133 | + (getCurrentInstance() |
| 134 | + ? inject(namespaceContextKey, ref(defaultNamespace)) |
| 135 | + : ref(defaultNamespace)) |
| 136 | + const namespace = computed(() => { |
| 137 | + return unref(derivedNamespace) || defaultNamespace |
| 138 | + }) |
| 139 | + return namespace |
| 140 | +} |
| 141 | + |
| 142 | +const _bem = ( |
| 143 | + namespace: string, |
| 144 | + block: string, |
| 145 | + blockSuffix: string, |
| 146 | + element: string, |
| 147 | + modifier: string |
| 148 | +) => { |
| 149 | + let cls = `${namespace}-${block}` |
| 150 | + if (blockSuffix) { |
| 151 | + cls += `-${blockSuffix}` |
| 152 | + } |
| 153 | + if (element) { |
| 154 | + cls += `__${element}` |
| 155 | + } |
| 156 | + if (modifier) { |
| 157 | + cls += `--${modifier}` |
| 158 | + } |
| 159 | + return cls |
| 160 | +} |
| 161 | + |
| 162 | +export const useNamespace = ( |
| 163 | + block: string, |
| 164 | + namespaceOverrides?: Ref<string | undefined> |
| 165 | +) => { |
| 166 | + const namespace = useGetDerivedNamespace(namespaceOverrides) |
| 167 | + const b = (blockSuffix = '') => |
| 168 | + _bem(namespace.value, block, blockSuffix, '', '') |
| 169 | + const e = (element?: string) => |
| 170 | + element ? _bem(namespace.value, block, '', element, '') : '' |
| 171 | + const m = (modifier?: string) => |
| 172 | + modifier ? _bem(namespace.value, block, '', '', modifier) : '' |
| 173 | + const be = (blockSuffix?: string, element?: string) => |
| 174 | + blockSuffix && element |
| 175 | + ? _bem(namespace.value, block, blockSuffix, element, '') |
| 176 | + : '' |
| 177 | + const em = (element?: string, modifier?: string) => |
| 178 | + element && modifier |
| 179 | + ? _bem(namespace.value, block, '', element, modifier) |
| 180 | + : '' |
| 181 | + const bm = (blockSuffix?: string, modifier?: string) => |
| 182 | + blockSuffix && modifier |
| 183 | + ? _bem(namespace.value, block, blockSuffix, '', modifier) |
| 184 | + : '' |
| 185 | + const bem = (blockSuffix?: string, element?: string, modifier?: string) => |
| 186 | + blockSuffix && element && modifier |
| 187 | + ? _bem(namespace.value, block, blockSuffix, element, modifier) |
| 188 | + : '' |
| 189 | + const is: { |
| 190 | + (name: string, state: boolean | undefined): string |
| 191 | + (name: string): string |
| 192 | + } = (name: string, ...args: [boolean | undefined] | []) => { |
| 193 | + const state = args.length >= 1 ? args[0]! : true |
| 194 | + return name && state ? `${statePrefix}${name}` : '' |
| 195 | + } |
| 196 | + |
| 197 | + // for css var |
| 198 | + // --el-xxx: value; |
| 199 | + const cssVar = (object: Record<string, string>) => { |
| 200 | + const styles: Record<string, string> = {} |
| 201 | + for (const key in object) { |
| 202 | + if (object[key]) { |
| 203 | + styles[`--${namespace.value}-${key}`] = object[key] |
| 204 | + } |
| 205 | + } |
| 206 | + return styles |
| 207 | + } |
| 208 | + // with block |
| 209 | + const cssVarBlock = (object: Record<string, string>) => { |
| 210 | + const styles: Record<string, string> = {} |
| 211 | + for (const key in object) { |
| 212 | + if (object[key]) { |
| 213 | + styles[`--${namespace.value}-${block}-${key}`] = object[key] |
| 214 | + } |
| 215 | + } |
| 216 | + return styles |
| 217 | + } |
| 218 | + |
| 219 | + const cssVarName = (name: string) => `--${namespace.value}-${name}` |
| 220 | + const cssVarBlockName = (name: string) => |
| 221 | + `--${namespace.value}-${block}-${name}` |
| 222 | + |
| 223 | + return { |
| 224 | + namespace, |
| 225 | + b, |
| 226 | + e, |
| 227 | + m, |
| 228 | + be, |
| 229 | + em, |
| 230 | + bm, |
| 231 | + bem, |
| 232 | + is, |
| 233 | + // css |
| 234 | + cssVar, |
| 235 | + cssVarName, |
| 236 | + cssVarBlock, |
| 237 | + cssVarBlockName, |
| 238 | + } |
| 239 | +} |
| 240 | +``` |
| 241 | +```js |
| 242 | +//拿affix组件举例 |
| 243 | +import { useNamespace } from '@element-plus/hooks' |
| 244 | +const ns = useNamespace('affix') |
| 245 | +<div ref="root" :class="ns.b()" :style="rootStyle"></div> |
| 246 | +``` |
| 247 | +
|
| 248 | +### :root |
| 249 | +:root 主要用于定义全局 CSS 变量,通过var读取全局属性 |
| 250 | +```css |
| 251 | +:root { |
| 252 | + --ep-c-bg-row: #f9fafc; |
| 253 | +} |
| 254 | +.row-bg { |
| 255 | + padding: 10px 0; |
| 256 | + background-color: var(--ep-c-bg-row); |
| 257 | +} |
| 258 | +``` |
0 commit comments