Config Provider 全局配置解析
官方文档:Config Provider 全局配置
1,介绍
在组件库的设计中,最常见的全局配置是
z-index
和
size
z-index
:统一管理弹出层(dialog, message, loading)等组件的层级。size
:统一管理表单元素大小。
这些都属于全局配置项,可以
Config Provider
全局配置来实现。
2,使用
全局配置项有2种引入方式。但都是调用同一个全局配置函数
provideGlobalConfig
实现的,后面会详细介绍。
1,完整引入
import{ createApp }from'vue'import ElementPlus from'element-plus'import App from'./App.vue'const app =createApp(App)
app.use(ElementPlus,{ size:'small', zIndex:3000})
相当于在
element-plus
的
install
方法中,调用了
provideGlobalConfig()
constinstall=(app, options)=>{// ...if(options)provideGlobalConfig(options, app,true)// ...}
2,按需引入
<template><el-config-provider:size="size":z-index="zIndex"><app/></el-config-provider></template><script>import{ defineComponent }from'vue'import{ ElConfigProvider }from'element-plus'exportdefaultdefineComponent({components:{
ElConfigProvider,},setup(){return{zIndex:3000,size:'small',}},})</script>
- 相当于在
el-config-provider
组件中,调用了provideGlobalConfig()
- 如果将根组件作为
el-config-provider
的子组件传入,也相当于全局引入。
el-config-provider
组件,传入指定的配置项
props
,在子组件中生效。
3,全局配置的实现
现在明确了:
- 完整引入通过
install
方法调用provideGlobalConfig()
, - 按需引入通过
el-config-provider
组件调用provideGlobalConfig()
,
install
方法没什么好说的,所以先介绍下
el-config-provider
组件,再来详细介绍
provideGlobalConfig()
。
3.1,el-config-provider
组件路径:
packages\components\config-provider\src\config-provider.ts
代码稍微做了简化。
import{ defineComponent, renderSlot }from'vue'import{ provideGlobalConfig }from'./hooks/use-global-config'const ConfigProvider =defineComponent({name:'ElConfigProvider',// locale | size | zIndex | namespace | button | message 等props: configProviderProps,setup(props,{ slots }){const config =provideGlobalConfig(props)return()=>renderSlot(slots,'default',{config: config?.value })},})exportdefault ConfigProvider
renderSlot
renderSlot()
和
h('div', xxx)
一样,会返回 VNode,但参数不一致。
**可以在
setup()
中返回渲染函数来创建组件**。
exportdeclarefunctionrenderSlot(
slots: Slots,
name:string,
props?: Data,
fallback?:()=> VNodeArrayChildren,
noSlotted?:boolean): VNode;
主要介绍前3个参数:
- slots:父组件传入的插槽内容。
- name:具名插槽的名称。
带 name 的插槽被称为具名插槽 (named slots)。没有提供 name 的 slot 出口会隐式地命名为“default”
所以
renderSlot(slots, 'default', { config: config?.value })
定义的是默认插槽。
- props:传给父组件的 props。
结合以上,
Config Provider
组件如果使用 template 定义,大致如下
<template><div><slot:config="config"></slot></div></template><scriptlang="ts"setup>const config =provideGlobalConfig(props)</script>
关于传给父组件的 props 的举例:
<!-- <MyComponent> 子组件 --><div><slot:text="greetingMessage":count="1"></slot></div><!-- 父组件 --><MyComponentv-slot="slotProps">
{{ slotProps.text }} {{ slotProps.count }}
</MyComponent>
3.2,provideGlobalConfig
先介绍依赖的变量和函数
1,定义全局配置对象
globalConfig
// 全局配置对象const globalConfig =ref()// 全局配置依赖注入 provide/inject 的 keyconst configProviderContextKey =Symbol()
2,获取全局配置的 hook 函数
useGlobalConfig()
直接调用,获取全局配置useGlobalConfig('namespace', 'el')
传参调用,获取指定的全局配置
import{ getCurrentInstance }from'vue'functionuseGlobalConfig(key, defaultValue =undefined){const config =getCurrentInstance()?inject(configProviderContextKey, globalConfig): globalConfig
if(key){returncomputed(()=> config.value?.[key]?? defaultValue)}else{return config
}}
3,合并(新旧)全局配置
// 新值 b 会覆盖旧值 aconstmergeConfig=(a, b)=>{const keys =[...newSet([...Object.keys(a),...Object.keys(b)])]const obj ={}for(const key of keys){
obj[key]= b[key]?? a[key]}return obj
}
4,provideGlobalConfig
有3个参数,是因为有2种调用方式。
- 按需引入,通过
el-config-provider
组件调用时,只会传入全局配置参数config
- 完整引入,通过 install 方法(作为插件)调用时,是应用层 Provide,所以还需要应用实例参数
app
,此时参数global = true
,会对全局配置对象globalConfig
赋值。
import{ getCurrentInstance }from'vue'constprovideGlobalConfig=(config, app, global =false)=>{// 在 setup() 或 <script setup> 中 getCurrentInstance() 才有值const inSetup =!!getCurrentInstance()const oldConfig = inSetup ?useGlobalConfig():undefined// 应用层 Provide 需要使用 app.provideconst 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
returnmergeConfig(oldConfig.value, cfg)})// 最核心的代码:provide 全局配置,这样才能在 useGlobalConfig() 中获取到全局配置provideFn(configProviderContextKey, context)// 下面这些都是 provide 指定的全局配置provideFn(
localeContextKey,// Symbol('localeContextKey')computed(()=> context.value.locale))provideFn(
namespaceContextKey,// Symbol('namespaceContextKey')computed(()=> context.value.namespace))provideFn(
zIndexContextKey,// Symbol('zIndexContextKey')computed(()=> context.value.zIndex))provideFn(SIZE_INJECTION_KEY,{// Symbol('size')size:computed(()=> context.value.size ||''),})// install 方法中调用 || 初次赋值if(global ||!globalConfig.value){
globalConfig.value = context.value
}return context
}
3.3,总结
- 在
provideGlobalConfig
中provide
的依赖,在使用的地方inject
即可。
举例
constuseLocale=(localeOverrides)=>{const locale = localeOverrides ||inject(localeContextKey,ref())// ...}
provideGlobalConfig
最终返回的context
会作为el-config-provider
组件的默认插槽的 props 使用。
举例:
<template><el-config-provider:size="size":z-index="zIndex"v-slot="{ config }"><div>{{ config.namespace }}</div><app/></el-config-provider></template>
以上。
版权归原作者 下雪天的夏风 所有, 如有侵权,请联系我们删除。