micro-zoe / micro-app

A simple, efficient and powerful micro front-end framework. 一款简约、高效、功能强大的微前端框架
https://micro-zoe.github.io/micro-app/
MIT License
5.62k stars 570 forks source link

多层嵌套时,A-B-C,A加载B,B加载C时,B卸载后重新运行会报标签已经定义,导致C不能正常加载 #1276

Open lilu0826 opened 4 months ago

lilu0826 commented 4 months ago

问题描述

问题的具体描述 多层嵌套时,A-B-C,A加载B,B加载C时,B卸载后重新运行会报标签已经定义,导致C不能正常加载

复现步骤

1.启动main-app,sub-app,sub-app2。 2.嵌套顺序为main-app->sub-app->sub-app2 3.打开主应用地址:http://localhost:3000/ 4.点击 打开sub-app 5.第一次可以正常渲染 image 5.点击man-app-page,再点击打开sub-app 6.sub-app2无法正常渲染,控制台报[micro-app] element micro-app-sub is already defined image

上传截图

请上传代码截图、控制台、终端等截图以帮助我们了解您的问题。

复现仓库

请提供一个精简的代码仓库,然后上传到自己的 github,以帮助我们复现您的问题。

https://github.com/lilu0826/micro-app-bug.git

环境信息

lilu0826 commented 4 months ago

B每次产生一个动态标签名可以解决该问题 microApp.start({ tagName: "micro-app-sub"+Date.now(), // 标签名称必须以 micro-app- 开头 });

dahui4dev commented 4 months ago

B每次产生一个动态标签名可以解决该问题 microApp.start({ tagName: "micro-app-sub"+Date.now(), // 标签名称必须以 micro-app- 开头 });

我们现在也碰到同样的问题,这搞了动态标签,使用的地方能方便使用吗?怎么拿到那个动态标签呢

lilu0826 commented 4 months ago

B每次产生一个动态标签名可以解决该问题 microApp.start({ tagName: "micro-app-sub"+Date.now(), // 标签名称必须以 micro-app- 开头 });

我们现在也碰到同样的问题,这搞了动态标签,使用的地方能方便使用吗?怎么拿到那个动态标签呢

把动态生成的标签名存起来哇,用的时候取出来动态渲染,VUE可以用component is动态渲染,React可以用createElement来渲染。

bailicangdu commented 4 months ago

将中间层应用B切换为umd模式即可,参考文档 https://micro-zoe.github.io/micro-app/docs.html#/zh-cn/framework/vue?id=_1%e3%80%81%e5%bc%80%e5%90%afumd%e6%a8%a1%e5%bc%8f%ef%bc%8c%e4%bc%98%e5%8c%96%e5%86%85%e5%ad%98%e5%92%8c%e6%80%a7%e8%83%bd, 代码参考

import "./assets/main.css";

import { createApp } from "vue";
import App from "./App.vue";
import * as VueRouter from 'vue-router'
import routes from "./router";

import microApp from "@micro-zoe/micro-app";

microApp.start({
    tagName: "micro-app-sub", // 标签名称必须以 `micro-app-` 开头
});

let app = null
let router = null
let history = null

// 👇 将渲染操作放入 mount 函数,子应用初始化时会自动执行
window.mount = () => {
    history = VueRouter.createWebHistory(window.__MICRO_APP_BASE_ROUTE__ || import.meta.env.BASE_URL)
    router = VueRouter.createRouter({
      history,
      routes,
    })

    app = createApp(App);
    app.use(router);
    app.mount("#app");
}

// 👇 将卸载操作放入 unmount 函数,就是上面步骤2中的卸载函数
window.unmount = () => {
    app.unmount()
    history.destroy()
    app = null
    router = null
    history = null
}

// 如果不在微前端环境,则直接执行mount渲染
if (!window.__MICRO_APP_ENVIRONMENT__) {
    window.mount()
}
lilu0826 commented 4 months ago

umd模式改造有点复杂,vue-router必须新创建一个,现在有其他的组件,pinia模块,以及ts文件都在引用router对象,重新创建的话其他模块的引用需要大量调整,我试了不重新创建router直接复用之前的好像渲染有问题。。

lilu0826 commented 4 months ago

iframe沙箱,window.mount好像都没执行

bailicangdu commented 4 months ago

umd模式是最优解了,无论是否是循环嵌套,都建议使用umd模式