使用h函数和render函数挂载组件,组件引用的全局UI组件无法正常解析渲染 #11213
Replies: 6 comments 1 reply
-
应该使用
|
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
你是用render进行挂载的吗,如果是render到某一个节点下,是不会获取到全局组件的 |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
Moved to discussions as this is not a bug. |
Beta Was this translation helpful? Give feedback.
-
We use the following solution in our internal component libraries to create the imperative Modal component . 1. Basic Modal Component StructureThe base Modal component follows a standard implementation with a <teleport>
<div>
<transition>
<div v-show="visible">
<slot />
</div>
</transition>
</div>
</teleport> 2. Global Hook DefinitionProvides an function useGlobalModals() {
const instance = getCurrentInstance()!;
function open(comp: CustomModal, componentProps?: Record<string, any>, detached?: boolean) {
return createModalInstance(comp, componentProps, instance, detached);
}
return { open }
} 3. Modal Instance Creation ImplementationDetailed implementation of the import type { Component, ComponentInternalInstance } from 'vue';
import { createVNode, getCurrentInstance, isReactive, nextTick, onBeforeUnmount, render, watch } from 'vue';
export function createModalInstance(
ModalComponent: Component,
componentProps: Record<string, any> = {},
parentInstance?: ComponentInternalInstance,
detached?: boolean,
): ModalInstance {
let vnode;
const ctxInstance = parentInstance ?? getCurrentInstance();
const deferred = Promise.withResolvers();
const container = document.createElement('div');
// Property name for controlling visibility
const visiblePropName = 'visible';
const show = () => {
if (vnode.component) vnode.component.props[visiblePropName] = true;
};
const close = () => {
if (vnode.component) vnode.component.props[visiblePropName] = false;
};
const destroy = () => {
render(null, container);
container.parentNode?.removeChild(container);
vnode = null;
};
// Modal event handlers
const modalListeners = {
'onHidden': destroy,
'onOk': (result: any) => {
close();
deferred.resolve(result);
},
'onCancel': () => {
close();
deferred.resolve(false);
},
'onClose': () => {
close();
deferred.resolve(undefined);
},
};
const rerender = (props: Record<string, any>) => {
if (vnode) {
render(null, container);
}
let vnodeHook = (vnodeComponent: any) => {
if (ctxInstance && !detached) {
// Rebuild provides and parent references between components
vnodeComponent.provides = Object.create(ctxInstance['provides']);
vnodeComponent.parent = ctxInstance;
onBeforeUnmount(() => {
nextTick(() => close());
}, ctxInstance);
vnodeHook = () => {};
}
};
vnode = createVNode(ModalComponent, {
...props,
[visiblePropName]: false,
...modalListeners,
});
vnode.appContext = ctxInstance!.appContext;
// Handle component instance reference:
// Note: Currently no other elegant way found to execute vnodeHook
// Using Object.defineProperty to intercept vnode.component to call vnodeHook function
// 👇🏼 1️⃣
Object.defineProperty(vnode, 'component', {
get() {
return this._component;
},
set(k) {
this._component = k;
vnodeHook(k);
},
});
nextTick(() => {
document.body.appendChild(container);
render(vnode, container);
setTimeout(() => show(), 0);
});
};
// Support for reactive props
if (isReactive(componentProps)) {
watch(componentProps, props => rerender(props), { deep: true });
}
rerender(componentProps);
return {
promise: deferred.promise,
close: (result?: any) => {
close();
deferred.resolve(result);
},
hide: close,
};
}
export interface ModalInstance {
close: (result?: any) => void;
hide: () => void;
promise: Promise<any>;
} Usage Exampleconst { open } = useGlobalModals();
await open(CustomModal, {
title: 'Example Modal',
content: 'Modal content here'
}).promise; The above works in practice and I would like to get feedback:1, what would be the potential problems with the above aspects |
Beta Was this translation helpful? Give feedback.
-
Vue version
3.3.9
Link to minimal reproduction
https://stackblitz.com/edit/vitejs-vite-twm44h?file=src%2Fcomponents%2Fcommand.js
Steps to reproduce
import { h, render } from 'vue'
// 弹层组件
const componentMap = {
CustomDialog: defineAsyncComponent(
() => import('@/components/CustomDialog.vue')
)
}
export default function useCommandDialog() {
function showDialog(config: IDialogCommandConfig) {
const component = componentMap[config.component]
const mountedEl = (config.container || document.body.querySelector('#app')) as HTMLElement
const componentVnode = h(component, { ...config.props })
console.log('commandDialog', componentVnode, component)
}
return {
showDialog
}
}
What is expected?
希望正常显示弹窗组件和组件引用的UI组件
What is actually happening?
报错:
Failed to resolve component: a-select
If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement
System Info
No response
Any additional comments?
No response
Beta Was this translation helpful? Give feedback.
All reactions