有没有办法在主机设置函数之外声明 Vue3 动态组件中的提供/注入?

问题描述 投票:0回答:2

我正在

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
对象中。

vue.js vuejs3
2个回答
0
投票

浏览完Vue3源代码后,无法直接在模板中将

provide
规范指示为
dynamic component
。 必须在托管动态组件的父级的设置函数或选项中,或者在动态组件的设置或选项中调用它。

这 2 个选项是:

  1. 您在托管动态组件的组件上调用
    provide
setup() {
  provide('message', 'hello')
}
<template>
  <component :is='myComponent' />
</template>

这对我不起作用,因为我的设置函数在我的动态组件被激活之前就已经被调用了;我还需要将组件类型和提供的值一起设置。

  1. 将要提供的项目作为 prop 发送到组件中,并让动态组件调用它们。
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)
  }
}

现在这有它的问题,因为每个动态组件现在都需要负责了解并调用传入的提供项。

解决方案1

解决每个组件需要了解所提供值的方法是创建一个提供值的中间组件。

可提供(中间组件)

<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>

解决方案2

更简洁的解决方案是创建一个包装器组件,类似于

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>

0
投票

我的没有提供注入机制,我对此进行了广泛的测试,并且它对我来说不够动态工作 - 它仅在父组件中渲染一次。因此,该解决方案侧重于

defineAsyncComponent
,它可以与 pinia 或事件相关地动态地工作。

解决方案3

异步加载组件列表:

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

我希望它有帮助:)

© www.soinside.com 2019 - 2024. All rights reserved.