From f6926d3e4e9fc4fc365a79a7b1499909d0fb6977 Mon Sep 17 00:00:00 2001 From: Linghao Su Date: Sun, 28 Jan 2024 23:02:56 +0800 Subject: [PATCH] fix(vue): add unmount hook to unmount application (#2849) --- .changeset/rich-parents-relate.md | 5 ++ examples/main/render/Vue3Render.js | 37 ++++++++-- packages/ui-bindings/vue/README.md | 1 + packages/ui-bindings/vue/README.zh-CN.md | 1 + packages/ui-bindings/vue/src/MicroApp.ts | 88 +++++++++++++++--------- 5 files changed, 93 insertions(+), 39 deletions(-) create mode 100644 .changeset/rich-parents-relate.md diff --git a/.changeset/rich-parents-relate.md b/.changeset/rich-parents-relate.md new file mode 100644 index 000000000..39258ed37 --- /dev/null +++ b/.changeset/rich-parents-relate.md @@ -0,0 +1,5 @@ +--- +"@qiankunjs/vue": patch +--- + +fix(vue): add unmount hook to unmount application diff --git a/examples/main/render/Vue3Render.js b/examples/main/render/Vue3Render.js index 077247689..7cd0281e8 100644 --- a/examples/main/render/Vue3Render.js +++ b/examples/main/render/Vue3Render.js @@ -1,6 +1,12 @@ -import { createApp, h } from 'vue'; +import { createApp, h, onBeforeMount, onBeforeUnmount, ref } from 'vue'; import { MicroApp } from '../../../packages/ui-bindings/vue/dist/esm'; +const sidemenu = document.querySelector('.mainapp-sidemenu'); + +const microApps = [ + { name: 'react15', entry: '//localhost:7102' }, + { name: 'react16', entry: '//localhost:7100' }, +]; function vueRender() { const application = createApp({ @@ -8,16 +14,35 @@ function vueRender() { }, render() { return h('div', [ - this.message, - h(MicroApp, { name: 'react15', entry: '//localhost:7102' }), - this.message, + h(MicroApp, { name: this.name, entry: this.entry, autoCaptureError: true }), ]); }, setup() { - const message = 'abc'; + const name = ref(''); + const entry = ref(''); + + const handleMenuClick = (e) => { + const app = microApps.find((app) => app.name === e.target.dataset.value); + if (app && app.name !== name.value) { + name.value = app.name; + entry.value = app.entry; + } else { + console.log('not found any app'); + } + } + + onBeforeMount(() => { + sidemenu.addEventListener('click', handleMenuClick); + }); + + + onBeforeUnmount(() => { + sidemenu.removeEventListener('click', handleMenuClick); + }); return { - message, + name, + entry, }; // return () => h('div', [ // message.value, diff --git a/packages/ui-bindings/vue/README.md b/packages/ui-bindings/vue/README.md index 99242fec4..95f8d6a5e 100644 --- a/packages/ui-bindings/vue/README.md +++ b/packages/ui-bindings/vue/README.md @@ -105,6 +105,7 @@ import { MicroApp } from '@qiankunjs/vue'; | `autoCaptureError` | No | Automatically set error capturing for the micro-application | `boolean` | `false` | | `className` | No | The style class for the micro-application | `string` | `undefined` | | `wrapperClassName` | No | The style class wrapping the micro-application's loading and error components | `string` | `undefined` | +| `appProps` | No | Properties passed to the sub-application | `Record` | `undefined` | ### Component Slots diff --git a/packages/ui-bindings/vue/README.zh-CN.md b/packages/ui-bindings/vue/README.zh-CN.md index e11c305a3..f6533d9e4 100644 --- a/packages/ui-bindings/vue/README.zh-CN.md +++ b/packages/ui-bindings/vue/README.zh-CN.md @@ -107,6 +107,7 @@ import { MicroApp } from '@qiankunjs/vue'; | `autoCaptureError` | 否 | 自动设置微应用的错误捕获 | `boolean` | `false` | | `className` | 否 | 微应用的样式类 | `string` | `undefined` | | `wrapperClassName` | 否 | 包裹微应用加载组件、错误捕获组件和微应用的样式类,仅在启用加载组件或错误捕获组件时有效 | `string` | `undefined` | +| `appProps` | 否 | 传递给子应用的属性 | `Record` | `undefined` | ### 组件插槽 diff --git a/packages/ui-bindings/vue/src/MicroApp.ts b/packages/ui-bindings/vue/src/MicroApp.ts index 0fa78cbbd..e9ebd1d1a 100644 --- a/packages/ui-bindings/vue/src/MicroApp.ts +++ b/packages/ui-bindings/vue/src/MicroApp.ts @@ -1,5 +1,16 @@ import type { PropType } from 'vue-demi'; -import { computed, defineComponent, h, onMounted, reactive, ref, toRefs, watch, isVue2 } from 'vue-demi'; +import { + computed, + defineComponent, + h, + onMounted, + ref, + onBeforeUnmount, + shallowRef, + toRefs, + watch, + isVue2, +} from 'vue-demi'; import type { AppConfiguration, LifeCycles } from 'qiankun'; import type { MicroAppType } from '@qiankunjs/ui-shared'; import { mountMicroApp, omitSharedProps, unmountMicroApp, updateMicroApp } from '@qiankunjs/ui-shared'; @@ -43,23 +54,23 @@ export const MicroApp = defineComponent({ type: String, default: undefined, }, + appProps: { + type: Object, + default: undefined, + }, }, setup(props, { slots }) { const originProps = props; - const { name, wrapperClassName, className, ...propsFromParams } = toRefs(originProps); + const { name, wrapperClassName, className, appProps, autoCaptureError } = toRefs(originProps); const loading = ref(false); const error = ref(); const containerRef = ref(null); - const microAppRef = ref(); - - const reactivePropsFromParams = computed(() => { - return omitSharedProps(reactive(propsFromParams)); - }); + const microAppRef = shallowRef(); const isNeedShowError = computed(() => { - return slots.errorBoundary || reactivePropsFromParams.value.autoCaptureError; + return slots.errorBoundary || autoCaptureError.value; }); // 配置了 errorBoundary 才改 error 状态,否则直接往上抛异常 @@ -77,31 +88,38 @@ export const MicroApp = defineComponent({ const rootRef = ref(null); - onMounted(() => { - console.log(rootRef.value); + const unmount = () => { + const microApp = microAppRef.value; - console.log(containerRef.value); + if (microApp) { + microApp._unmounting = true; + + unmountMicroApp(microApp).catch((err: Error) => { + setComponentError(err); + loading.value = false; + }); + + microAppRef.value = undefined; + } + }; + onMounted(() => { + // watch name 变更切换子应用 watch( name, () => { - const microApp = microAppRef.value; - - if (microApp) { - microApp._unmounting = true; - - unmountMicroApp(microApp).catch((err: Error) => { - setComponentError(err); - loading.value = false; - }); - - microAppRef.value = undefined; - } + const prevApp = microAppRef.value; + // 销毁上一个子应用 + unmount(); + // 初始化下一个子应用 void mountMicroApp({ - prevMicroApp: microAppRef.value, + prevMicroApp: prevApp, container: containerRef.value!, - componentProps: originProps, + componentProps: { + ...originProps, + ...appProps.value, + }, setLoading: (l) => { loading.value = l; }, @@ -118,13 +136,17 @@ export const MicroApp = defineComponent({ ); watch( - reactivePropsFromParams, + appProps, () => { updateMicroApp({ microApp: microAppRef.value, setLoading: (l) => { loading.value = l; }, + microAppProps: { + ...omitSharedProps(originProps), + ...appProps.value, + }, }); }, { @@ -133,6 +155,10 @@ export const MicroApp = defineComponent({ ); }); + onBeforeUnmount(() => { + unmount(); + }); + const microAppWrapperClassName = computed(() => wrapperClassName.value ? `${wrapperClassName.value} qiankun-micro-app-wrapper` : 'qiankun-micro-app-wrapper', ); @@ -149,16 +175,12 @@ export const MicroApp = defineComponent({ microAppWrapperClassName, microAppClassName, rootRef, - reactivePropsFromParams, microApp: microAppRef, }; }, render() { - return this.reactivePropsFromParams.autoSetLoading || - this.reactivePropsFromParams.autoCaptureError || - this.$slots.loader || - this.$slots.errorBoundary + return this.autoSetLoading || this.autoCaptureError || this.$slots.loader || this.$slots.errorBoundary ? h( 'div', { @@ -169,7 +191,7 @@ export const MicroApp = defineComponent({ ? typeof this.$slots.loader === 'function' ? this.$slots.loader(this.loading) : this.$slots.loader - : this.reactivePropsFromParams.autoSetLoading && + : this.autoSetLoading && h(MicroAppLoader, { ...(isVue2 ? { @@ -186,7 +208,7 @@ export const MicroApp = defineComponent({ ? typeof this.$slots.errorBoundary === 'function' ? this.$slots.errorBoundary(this.error) : this.$slots.errorBoundary - : this.reactivePropsFromParams.autoCaptureError && + : this.autoCaptureError && h(ErrorBoundary, { ...(isVue2 ? {