使用h函数和render函数挂载组件,组件引用的全局UI组件无法正常解析渲染 #11213
Unanswered
TurbMZ
asked this question in
Help/Questions
Replies: 7 comments 1 reply
|
Moved to discussions as this is not a bug. |
0 replies
|
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 |
0 replies
|
上面那哥们的component获取方式帮大忙了,补充下如果父级instance想从#app的 |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment










Uh oh!
There was an error while loading. Please reload this page.
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
All reactions