我正在
dynamic component
中创建一个 Vue3
。 我使用 props
提供 v-bind
。
<component :is='MyComponent' v-bind='myProps' />
我想使用
provide/inject
功能。 如何将我提供的属性放入动态组件中。 我的动态组件在 inject
函数中调用 setup
,并期望其子组件的值为 provided
。
虽然这在 Vue 上没有记录,但我尝试过但没有成功:
<component :is='MyComponent' v-bind='myProps' :provide='myProvidedProps'/>
甚至尝试将
provide
对象放入 props
对象中。
浏览完Vue3源代码后,无法直接在模板中将
provide
规范指示为dynamic component
。 必须在托管动态组件的父级的设置函数或选项中,或者在动态组件的设置或选项中调用它。
这 2 个选项是:
provide
。setup() {
provide('message', 'hello')
}
<template>
<component :is='myComponent' />
</template>
这对我不起作用,因为我的设置函数在我的动态组件被激活之前就已经被调用了;我还需要将组件类型和提供的值一起设置。
function setComponent(someImportedComponent, providedValues) {
myComponent.value = someImportedComponent
myProps.value = {
toProvide: providedValues
}
}
<template>
<component :is='myComponent' v-bind='myProps' />
</template>
我的组件
setup() {
for(let [key,value] of Object.entries(props.toProvide) ) {
provide(key, value)
}
}
现在这有它的问题,因为每个动态组件现在都需要负责了解并调用传入的提供项。
解决每个组件需要了解所提供值的方法是创建一个提供值的中间组件。
可提供(中间组件)
<script setup lang="ts">
import {provide} from 'vue'
const props = defineProps<{
is: any
provide?: Record<string, any>
[key: string]: any
}>()
if (props.provide) {
for (const [key, value] of Object.entries(props.provide)) {
provide(key, value)
}
}
const _props = Object.fromEntries(Object.entries(props).filter(it => {
return it[0] !== 'is' && it[0] !== 'provide'
}))
</script>
<template>
<component :is="is" v-bind="_props"/>
</template>
像这样使用它:
<template>
<providable :is="myComponent" :provide='toProvide' v-bind='myProps' />
</template>
更简洁的解决方案是创建一个包装器组件,类似于
keep-alive
的工作方式。 目标组件只需放入 default slot
.
提供.vue
<script setup lang="ts">
import {provide} from 'vue'
const props = defineProps<{
value: Record<string, any>
}>()
for (const [key, value] of Object.entries(props.value)) {
provide(key, value)
}
</script>
<template>
<slot name="default"/>
</template>
并像这样使用它:
<template>
<provide value='toProvide'>
<my-component v-bind='myProps' />
</provide>
</template>
我的没有提供注入机制,我对此进行了广泛的测试,并且它对我来说不够动态工作 - 它仅在父组件中渲染一次。因此,该解决方案侧重于
defineAsyncComponent
,它可以与 pinia 或事件相关地动态地工作。
异步加载组件列表:
import { defineAsyncComponent } from 'vue'
const DomainTestGroup_1_domain_A = () => import('@/components/domain/partials/DomainTestGroup_1_domain_A.vue')
// ...
export const dynamicLoadList = {
DomainTestGroup_1: {
DOMAIN_A: defineAsyncComponent(DomainTestGroup_1_domain_A),
DOMAIN_B: defineAsyncComponent(DomainTestGroup_1_domain_B)
},
DomainTestGroup_2: {
DOMAIN_B: defineAsyncComponent(DomainTestGroup_2_domain_B)
},
}
export type DynamicLoadGroups = keyof typeof dynamicLoadList
通用包装组件(注意:Vue 3.3+ 是必须的)
<template>
<component
:is="_componentToRender"
v-bind="props.internalProps"
/>
</template>
<script setup lang="ts" generic="G extends DynamicLoadGroups">
import { storeToRefs } from 'pinia'
import { shallowRef, watchEffect } from 'vue'
import { dynamicLoadList } from '../dynamicLoadList'
import { useLocalStorageStore } from './stores/localStorageDomain.store'
import type { CombinedComponentsProps, DynamicLoadGroups } from '.dynamicLoadList'
// this types can be quite tedious to create but you can just use traditional defineProps() without types
import type { CombinedComponentsProps, FilterComponents } from './types/dynamicLoadFilters'
const props = defineProps<{
groupName: G
internalProps: CombinedComponentsProps<G>
}>()
// DOMAIN_A or DOMAIN_B - pinia here can be exchanged with something else but it has to be reactive
const { getDomainKey } = storeToRefs(useLocalStorageStore())
const _componentToRender = shallowRef()
watchEffect(async () => {
const dynamicLoadGroup = dynamicLoadList[props.groupName]
const domainSpecificComponent = getDomainKey.value
? (dynamicLoadGroup as FilterComponents<G>)[getDomainKey.value]
: null
if (!domainSpecificComponent) console.info('Domain specific component not found')
_componentToRender.value = (await domainSpecificComponent)
})
</script>
如果操作正确,它也可以完全键入并连接到 pinia。如果您只是想测试它,请查看我的示例存储库:https://github.dev/Nagell/dynamic_loading_components
我希望它有帮助:)