Skip to content

Commit 91ed679

Browse files
committed
fix: unwrap reactive component definitions with toRaw at the render boundary
The admin resolver (useDataResolver) and ResourceManager's selected tab pass a component definition that lives in a reactive ref straight to h()/`<component :is>`. Vue warns ("received a Component that was made a reactive object") and pays needless Proxy overhead. toRaw the definition at the point of render — the containing ref stays fully reactive, only the component handed to the renderer is raw. No behaviour change; removes the warnings + overhead.
1 parent 012031e commit 91ed679

2 files changed

Lines changed: 12 additions & 3 deletions

File tree

src/runtime/templates/components/core/useDataResolver.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
defineComponent,
44
h, onBeforeUnmount, onMounted,
55
ref,
6+
toRaw,
67
watch,
78
render,
89
} from 'vue'
@@ -25,10 +26,14 @@ export const useDataResolver = <T extends object>(allMeta: Ref<(T | null)[]>, op
2526
(props: { component: ManagerTab, cProps: any }, { expose }) => {
2627
const metadata = ref<T | null>(null)
2728
const resolved = ref(false)
29+
// `props.component` arrives as a reactive proxy (it lives in the reactive `components` ref of
30+
// the caller). A component definition needs no reactivity, and handing a reactive object to
31+
// `h()` triggers a Vue perf warning — so unwrap to the raw definition. The caller's ref stays
32+
// fully reactive; only the component object passed to the renderer is raw.
2833
const possibleAsyncDefinition: ReturnType<typeof defineAsyncComponent> | undefined
2934
= typeof props.component === 'string'
3035
? globalComponents[props.component]
31-
: props.component
36+
: toRaw(props.component)
3237

3338
if (possibleAsyncDefinition === undefined) {
3439
throw new Error('Cannot load metadata for component')

src/runtime/templates/components/main/admin/resource-manager/ResourceManager.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts" setup>
2-
import { computed, nextTick, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue'
2+
import { computed, nextTick, onBeforeUnmount, onMounted, ref, toRaw, useTemplateRef, watch } from 'vue'
33
import ResourceLoadingIndicator
44
from '../_common/ResourceLoadingIndicator.vue'
55
import ManagerTabs from './_parts/ManagerTabs.vue'
@@ -103,7 +103,11 @@ const showSpacer = computed(() => {
103103
})
104104
105105
const selectedTab = computed(() => {
106-
return currentStackItem.value?.managerTabs?.[selectedIndex.value]
106+
// the tab component lives in the reactive `currentStackItem`; hand `<component :is>` the raw
107+
// definition so Vue doesn't render (and warn about) a reactive component object. No reactivity
108+
// change — the stack item stays reactive, only the rendered component reference is raw.
109+
const tab = currentStackItem.value?.managerTabs?.[selectedIndex.value]
110+
return tab ? toRaw(tab) : tab
107111
})
108112
109113
watch([spacer, managerHolder, currentStackItem, selectedIndex, allTabsMeta], () => {

0 commit comments

Comments
 (0)