Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request] 主应用多页签切换不同子应用的页面状态保持 #361

Closed
gaorubin1990 opened this issue Mar 30, 2020 · 89 comments

Comments

@gaorubin1990
Copy link

Background

想要主应用负责导航及页面页签切换,切换页签,即切换不同子应用的页面时,container不重新覆盖。看了下singular: false适用于一个路由页面组合展示的不同子应用界面场景?能否指导一下该需求的实现思路,非常感谢

Proposal

Describe the solution you'd like, better to provide some pseudo code.

Additional context

Add any other context or screenshots about the feature request here.

@wisdomG
Copy link

wisdomG commented Mar 30, 2020

我也有同样的需求。不知道用qiankun怎么实现,暂时只能用frame去做了

@werts
Copy link

werts commented Apr 2, 2020

@gaorubin1990 @wisdomG 找到方案了吗?

@anncwb
Copy link

anncwb commented Apr 2, 2020

同求,一直不敢下手,就是因为这个

@kuitos
Copy link
Member

kuitos commented Apr 2, 2020

@wisdomG
Copy link

wisdomG commented Apr 2, 2020

see https://qiankun.umijs.org/zh/faq/#%E5%A6%82%E4%BD%95%E5%90%8C%E6%97%B6%E6%BF%80%E6%B4%BB%E4%B8%A4%E4%B8%AA%E5%AD%90%E5%BA%94%E7%94%A8%EF%BC%9F

不同的子应用的 render 实现不一样就不会覆盖了

我试着把react16和react15的activeRule,改成genActiveRule(/react),也改了singular为false,但访问/react16这个路由的时候,会报错
image

@gaorubin1990
Copy link
Author

主应用把标签信息存在了本地存储,然后子应用的container都存在了主应用一个对象里,一个应用一个key,切换路由即切换应用时切换container,子应用的unmonunt函数不销毁子应用。子应用用的keepalive缓存组件状态。子应用通过监听路由变化确定什么时候取消缓存并修改主应用的标签本地缓存信息。这样表面上是实现了,不知道有没有什么性能问题。
常见问题里同时激活两个子应用的场景感觉不太适合多页签场景, @kuitos 能否知道下具体实现该场景的思路呢

@see365
Copy link

see365 commented Apr 11, 2020

不行 第一个子应用请求js 域名错误 请求到第二个子应用的域名下了。。。。。

@see365
Copy link

see365 commented Apr 11, 2020

see https://qiankun.umijs.org/zh/faq/#%E5%A6%82%E4%BD%95%E5%90%8C%E6%97%B6%E6%BF%80%E6%B4%BB%E4%B8%A4%E4%B8%AA%E5%AD%90%E5%BA%94%E7%94%A8%EF%BC%9F

不同的子应用的 render 实现不一样就不会覆盖了
不行 第一个子应用请求js 域名错误 请求到第二个子应用的域名下了。。。。。

@Clive-Wang
Copy link

有没有哪位大佬解决了这个问题啊

@Liaoct
Copy link

Liaoct commented Apr 15, 2020

我解决这个问题了。我的主应用是JQuery(因为一些技术原因暂时保留JQuery),子应用是Vue。Tab切换,会为每个tab页创建一个div容器,我们的子应用页面(Vue)显示在容器中,相应的其他tab页被隐藏,同时切换路由。要实现tab页切换,并保持页面缓存功能,可以换个思路。Vue的keep-alive可以做页面缓存,并且Vue只有一个根实例对象,挂载在一个dom节点上,其内部状态都在实例对象上。因此,在tab切换时,我们可以将dom节点,从一个容器移动到另一个容器,同时随着路由的切换,页面实现了切换,并且keep-alive生效。
对于React也可以参考实现keep-alive。

@werts
Copy link

werts commented Apr 15, 2020

angular的有思路吗?

@Clive-Wang
Copy link

能提供个例子,参考下吗?

@penxu
Copy link

penxu commented Apr 18, 2020

@Liaoct 有例子看下吗?我正好有这个需求

@Liaoct
Copy link

Liaoct commented Apr 20, 2020

我的Vue子应用可以实现Tab切换并保持页面状态,代码如下:

let instance = null;
let router = null;

function render() {
  // 这里必须要new一个新的路由实例,否则无法响应URL的变化。
  router = new VueRouter({
    mode: "hash",
    base: process.env.BASE_URL,
    routes
  });
  if (
    window.__POWERED_BY_QIANKUN__ &&
    window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__
  ) {
    const cachedInstance = window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__;

    // 从最初的Vue实例上获得_vnode
    const cachedNode =
      // (cachedInstance.cachedInstance && cachedInstance.cachedInstance._vnode) ||
      cachedInstance._vnode;

    // 让当前路由在最初的Vue实例上可用
    router.apps.push(...cachedInstance.$router.apps);
    // keepAlive可用
    cachedNode.data.keepAlive = true;

    instance = new Vue({
      router,
      store,
      render: () => cachedNode
    });

    // 缓存最初的Vue实例
    instance.cachedInstance = cachedInstance;

    const { path } = router.currentRoute;
    const { path: oldPath } = cachedInstance.$router.currentRoute;
    // 当前路由和上一次卸载时不一致,则切换至新路由
    if (path !== oldPath) {
      cachedInstance.$router.push(path);
    }
    instance.$mount("#app");
  } else {
    // 正常实例化
    instance = new Vue({
      router,
      store,
      render: h => h(App)
    }).$mount("#app");
  }
}

if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

export async function bootstrap() {
  console.log("[vue] vue app bootstraped");
}

export async function mount(props) {
  console.log("[vue] props from main framework", props);
  render(props);
}

export async function unmount() {
  console.log("[vue] vue app unmount");
  window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__ =
    instance.cachedInstance || instance;
  // 不销毁实例
  // instance.$destroy();
  // instance = null;
  router = null;
}

@penxu
Copy link

penxu commented Apr 21, 2020

window.CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE =
instance.cachedInstance || instance;

子应用实例都挂载在window,那多个子应用不是会被覆盖吗?

@Liaoct
Copy link

Liaoct commented Apr 21, 2020

window.CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE =
instance.cachedInstance || instance;

子应用实例都挂载在window,那多个子应用不是会被覆盖吗?

qiankun为每个子应用创建了一个沙箱环境,子应用的window实际上是window.proxy。另外,你也可以不挂载到window,实例也会一直存在的,加载过的子应用不会再次加载。

@Liaoct
Copy link

Liaoct commented Apr 21, 2020

const { path } = router.currentRoute;
const { path: oldPath } = cachedInstance.$router.currentRoute;
// 当前路由和上一次卸载时不一致,则切换至新路由
if (path !== oldPath) {
  cachedInstance.$router.push(path);
}

当Router导航未完成就进行路由修正可能会引发路由不匹配的问题。修改方式如下:

router.onReady(() => {
      const { path } = router.currentRoute;
      const { path: oldPath } = cachedInstance.$router.currentRoute;
      // 当前路由和上一次卸载时不一致,则切换至新路由
      if (path !== oldPath) {
        cachedInstance.$router.push(path);
      }
});

@penxu
Copy link

penxu commented Apr 21, 2020

window.CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE =
instance.cachedInstance || instance;

子应用实例都挂载在window,那多个子应用不是会被覆盖吗?

qiankun为每个子应用创建了一个沙箱环境,子应用的window实际上是window.proxy。另外,你也可以不挂载到window,实例也会一直存在的,加载过的子应用不会再次加载。

那这样,父应用要想获取子应用的一下属性,比如路由name来表示window.title,有什么办法吗?

@theshying
Copy link

我的Vue子应用可以实现Tab切换并保持页面状态,代码如下:

let instance = null;
let router = null;

function render() {
  // 这里必须要new一个新的路由实例,否则无法响应URL的变化。
  router = new VueRouter({
    mode: "hash",
    base: process.env.BASE_URL,
    routes
  });
  if (
    window.__POWERED_BY_QIANKUN__ &&
    window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__
  ) {
    const cachedInstance = window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__;

    // 从最初的Vue实例上获得_vnode
    const cachedNode =
      // (cachedInstance.cachedInstance && cachedInstance.cachedInstance._vnode) ||
      cachedInstance._vnode;

    // 让当前路由在最初的Vue实例上可用
    router.apps.push(...cachedInstance.$router.apps);
    // keepAlive可用
    cachedNode.data.keepAlive = true;

    instance = new Vue({
      router,
      store,
      render: () => cachedNode
    });

    // 缓存最初的Vue实例
    instance.cachedInstance = cachedInstance;

    const { path } = router.currentRoute;
    const { path: oldPath } = cachedInstance.$router.currentRoute;
    // 当前路由和上一次卸载时不一致,则切换至新路由
    if (path !== oldPath) {
      cachedInstance.$router.push(path);
    }
    instance.$mount("#app");
  } else {
    // 正常实例化
    instance = new Vue({
      router,
      store,
      render: h => h(App)
    }).$mount("#app");
  }
}

if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

export async function bootstrap() {
  console.log("[vue] vue app bootstraped");
}

export async function mount(props) {
  console.log("[vue] props from main framework", props);
  render(props);
}

export async function unmount() {
  console.log("[vue] vue app unmount");
  window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__ =
    instance.cachedInstance || instance;
  // 不销毁实例
  // instance.$destroy();
  // instance = null;
  router = null;
}

这个加载过的子应用实例会一致存在,不会被销毁,当有些情况下需要销毁掉子应用实例,重新生成一个全新的,有什么好的方法吗

@Liaoct
Copy link

Liaoct commented Apr 23, 2020

我的Vue子应用可以实现Tab切换并保持页面状态,代码如下:

let instance = null;
let router = null;

function render() {
  // 这里必须要new一个新的路由实例,否则无法响应URL的变化。
  router = new VueRouter({
    mode: "hash",
    base: process.env.BASE_URL,
    routes
  });
  if (
    window.__POWERED_BY_QIANKUN__ &&
    window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__
  ) {
    const cachedInstance = window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__;

    // 从最初的Vue实例上获得_vnode
    const cachedNode =
      // (cachedInstance.cachedInstance && cachedInstance.cachedInstance._vnode) ||
      cachedInstance._vnode;

    // 让当前路由在最初的Vue实例上可用
    router.apps.push(...cachedInstance.$router.apps);
    // keepAlive可用
    cachedNode.data.keepAlive = true;

    instance = new Vue({
      router,
      store,
      render: () => cachedNode
    });

    // 缓存最初的Vue实例
    instance.cachedInstance = cachedInstance;

    const { path } = router.currentRoute;
    const { path: oldPath } = cachedInstance.$router.currentRoute;
    // 当前路由和上一次卸载时不一致,则切换至新路由
    if (path !== oldPath) {
      cachedInstance.$router.push(path);
    }
    instance.$mount("#app");
  } else {
    // 正常实例化
    instance = new Vue({
      router,
      store,
      render: h => h(App)
    }).$mount("#app");
  }
}

if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

export async function bootstrap() {
  console.log("[vue] vue app bootstraped");
}

export async function mount(props) {
  console.log("[vue] props from main framework", props);
  render(props);
}

export async function unmount() {
  console.log("[vue] vue app unmount");
  window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__ =
    instance.cachedInstance || instance;
  // 不销毁实例
  // instance.$destroy();
  // instance = null;
  router = null;
}

这个加载过的子应用实例会一致存在,不会被销毁,当有些情况下需要销毁掉子应用实例,重新生成一个全新的,有什么好的方法吗

这个条件一般都是来自主应用吧?那就是应用间通信的问题了。如果是子应用控制的,那就是在重新执行mount生命周期时加个控制参数。

@Liaoct
Copy link

Liaoct commented Apr 23, 2020

对于React子应用,我通过react-activation,也实现了页面缓存,暂时还没发现什么问题。Angular不太熟悉,没啥建议了。

@theshying
Copy link

theshying commented Apr 23, 2020

我的主子应用都是vue的,目前的场景是这样的,主应用是后台多tab的应用,每个tab都加载子应用的不同界面,现在想要达到的效果是,切换tab时缓存住子应用的界面,这点你上面的建议已经能实现,现在最重要的问题是,当我关闭了一个tab,就需要将这个tab的界面从子应用实例中的缓存中移除,不需要再缓存了,好像没有太好的解决办法

@tianctrl
Copy link

tianctrl commented Apr 28, 2020

对于React子应用,我通过react-activation,也实现了页面缓存,暂时还没发现什么问题。Angular不太熟悉,没啥建议了。

@Liaoct
react的实现方法, 可以指点一下么

@tianctrl
Copy link

react有什么解决方案么 各位

@Liaoct
Copy link

Liaoct commented May 8, 2020

抱歉各位,最近比较忙,手上几个项目跟着的,React缓存还没研究完。上次Vue的方案,还有一点问题,activateddeactivated不会触发。修改如下:

let instance = null;
let router = null;

/* eslint-disable no-underscore-dangle */

function render() {
  // 这里必须要new一个新的路由实例,否则无法响应URL的变化。
  router = new VueRouter({
    mode: 'hash',
    base: process.env.BASE_URL,
    routes
  });

  if (window.__POWERED_BY_QIANKUN__ && window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__) {
    const cachedInstance = window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__;

    // 从最初的Vue实例上获得_vnode
    const cachedNode =
      // (cachedInstance.cachedInstance && cachedInstance.cachedInstance._vnode) ||
      cachedInstance._vnode;

    // 让当前路由在最初的Vue实例上可用
    router.apps.push(...cachedInstance.$router.apps);

    instance = new Vue({
      router,
      store,
      i18n,
      render: () => cachedNode
    });

    // 缓存最初的Vue实例
    instance.cachedInstance = cachedInstance;

    router.onReady(() => {
      const { path } = router.currentRoute;
      const { path: oldPath } = cachedInstance.$router.currentRoute;
      // 当前路由和上一次卸载时不一致,则切换至新路由
      if (path !== oldPath) {
        cachedInstance.$router.push(path);
      }
    });
    instance.$mount('#app');
  } else {
    // 正常实例化
    instance = new Vue({
      router,
      store,
      i18n,
      render: h => h(App)
    }).$mount('#app');
  }
}

if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

export async function bootstrap() {
  console.log('[vue] vue app bootstraped');
}

export async function mount(props) {
  console.log('[vue] props from main framework', props);
  render();
}

export async function unmount() {
  console.log('[vue] vue app unmount');
  const cachedInstance = instance.cachedInstance || instance;
  window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__ = cachedInstance;
  const cachedNode = cachedInstance._vnode;
  // 让keep-alive可用
  cachedNode.data.keepAlive = true;
  // 卸载当前实例,缓存的实例由于keep-alive生效,将不会真正被销毁,从而触发activated与deactivated
  instance.$destroy();
  router = null;
}

@zjhr
Copy link

zjhr commented Jun 9, 2020

抱歉各位,最近比较忙,手上几个项目跟着的,React缓存还没研究完。上次Vue的方案,还有一点问题,activateddeactivated不会触发。修改如下:

let instance = null;
let router = null;

/* eslint-disable no-underscore-dangle */

function render() {
  // 这里必须要new一个新的路由实例,否则无法响应URL的变化。
  router = new VueRouter({
    mode: 'hash',
    base: process.env.BASE_URL,
    routes
  });

  if (window.__POWERED_BY_QIANKUN__ && window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__) {
    const cachedInstance = window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__;

    // 从最初的Vue实例上获得_vnode
    const cachedNode =
      // (cachedInstance.cachedInstance && cachedInstance.cachedInstance._vnode) ||
      cachedInstance._vnode;

    // 让当前路由在最初的Vue实例上可用
    router.apps.push(...cachedInstance.$router.apps);

    instance = new Vue({
      router,
      store,
      i18n,
      render: () => cachedNode
    });

    // 缓存最初的Vue实例
    instance.cachedInstance = cachedInstance;

    router.onReady(() => {
      const { path } = router.currentRoute;
      const { path: oldPath } = cachedInstance.$router.currentRoute;
      // 当前路由和上一次卸载时不一致,则切换至新路由
      if (path !== oldPath) {
        cachedInstance.$router.push(path);
      }
    });
    instance.$mount('#app');
  } else {
    // 正常实例化
    instance = new Vue({
      router,
      store,
      i18n,
      render: h => h(App)
    }).$mount('#app');
  }
}

if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

export async function bootstrap() {
  console.log('[vue] vue app bootstraped');
}

export async function mount(props) {
  console.log('[vue] props from main framework', props);
  render();
}

export async function unmount() {
  console.log('[vue] vue app unmount');
  const cachedInstance = instance.cachedInstance || instance;
  window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__ = cachedInstance;
  const cachedNode = cachedInstance._vnode;
  // 让keep-alive可用
  cachedNode.data.keepAlive = true;
  // 卸载当前实例,缓存的实例由于keep-alive生效,将不会真正被销毁,从而触发activated与deactivated
  instance.$destroy();
  router = null;
}

这个方案好像也会有问题,场景是这样到,子应用有两个路由a,b,目前从子应用路由a去到主应用路由后,回到子应用路由a,a切换到路由b会出现一个问题,子应用到内容还是a的。然后我发现这个问题只有子应用真正执行了instance.$destroy()后才不会有这种内容不变的情况。请教一下,要做到可以缓存而且不会出现以上的情况应该怎么处理?

@tzcodingjs
Copy link

所以这一块具体是如何解决呢?通过标签页缓存子应用的页面。

@Tinet-zhangmd
Copy link

angular的有思路吗?

请问您现在实现了吗?

@kuaifengle
Copy link

kuaifengle commented Feb 17, 2022

参考代码地址: https://github.com/kuaifengle/qiankun-vue3-tabsPage
网页演示网址: https://kuaifengle.github.io/qiankun-vue3-tabsPage-demo

@zhaoxu666
Copy link

这个暂时没有,我们项目已经基本稳定了,各种复杂tabs已经处理完备了

您好, 您的实现方案有Demo吗? 十分感谢

@xinb-yanh
Copy link

棱角分明的有什么联想吗?

请问angualr您是如何实现的

@xinb-yanh
Copy link

angular的有什么想法吗?

请问您现在实现了吗?

请问您这边实现了吗?

@xiaoqufengdi
Copy link

我的Vue子应用可以实现Tab切换并保持页面状态,代码如下:

let instance = null;
let router = null;

function render() {
  // 这里必须要new一个新的路由实例,否则无法响应URL的变化。
  router = new VueRouter({
    mode: "hash",
    base: process.env.BASE_URL,
    routes
  });
  if (
    window.__POWERED_BY_QIANKUN__ &&
    window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__
  ) {
    const cachedInstance = window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__;

    // 从最初的Vue实例上获得_vnode
    const cachedNode =
      // (cachedInstance.cachedInstance && cachedInstance.cachedInstance._vnode) ||
      cachedInstance._vnode;

    // 让当前路由在最初的Vue实例上可用
    router.apps.push(...cachedInstance.$router.apps);
    // keepAlive可用
    cachedNode.data.keepAlive = true;

    instance = new Vue({
      router,
      store,
      render: () => cachedNode
    });

    // 缓存最初的Vue实例
    instance.cachedInstance = cachedInstance;

    const { path } = router.currentRoute;
    const { path: oldPath } = cachedInstance.$router.currentRoute;
    // 当前路由和上一次卸载时不一致,则切换至新路由
    if (path !== oldPath) {
      cachedInstance.$router.push(path);
    }
    instance.$mount("#app");
  } else {
    // 正常实例化
    instance = new Vue({
      router,
      store,
      render: h => h(App)
    }).$mount("#app");
  }
}

if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

export async function bootstrap() {
  console.log("[vue] vue app bootstraped");
}

export async function mount(props) {
  console.log("[vue] props from main framework", props);
  render(props);
}

export async function unmount() {
  console.log("[vue] vue app unmount");
  window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__ =
    instance.cachedInstance || instance;
  // 不销毁实例
  // instance.$destroy();
  // instance = null;
  router = null;
}

如果是react子应用呢?

@dengBox
Copy link

dengBox commented Jul 11, 2022

react最近刚做完,系统内的缓存使用react-activation,系统外的直接缓存react实例

@zhoubaibai
Copy link

react最近刚做完,系统内的缓存使用react-activation,系统外的直接缓存react实例
大佬 react切换的能提供个例子吗

@dengBox
Copy link

dengBox commented Jul 21, 2022

react最近刚做完,系统内的缓存使用react-activation,系统外的直接缓存react实例
大佬 react切换的能提供个例子吗

只能提供一些伪代码了

export const catchNode = (changeCatch = true) => {
  if (changeCatch) needCatch = false;
  // node = (document.querySelector('#app') as Node)?.cloneNode(true);
  node = document.querySelector('#app');
};

let needCatch;
let node;

export async function bootstrap({ func = {} } = {}) {

  // 绑定容器函数
  window.func = func;
}

export async function mount() {
  console.log('[react] app mount');
  needCatch = true;
  if (!node) {
    runApp(appConfig);
  } else {
    // 跨系统之后 dom绑定事件不生效,因为是cloneNode。
    const oldNode = (document.querySelector('#app') as Node);
    oldNode.parentNode?.replaceChild(node, oldNode);
  }
}

export async function unmount(props) {
  console.log('[react] app unmount');
  if (needCatch) catchNode(false);
  ReactDOM.unmountComponentAtNode((props?.container || document.querySelector('#app')) as HTMLElement);
}

if (!window.__POWERED_BY_QIANKUN__) {
  // 本地测试
  bootstrap();
  mount();
}

@RainManGO
Copy link

手动load微前端,然后tabs 直接 缓存微前端的dom,tab关闭手动卸载。

@github-actions
Copy link

Since the issue was labeled with out-of-scope, but no response in 30 days. This issue will be close. If you have any questions, you can comment and reply.
由于该 issue 被标记为与本项目无关,却 30 天未收到回应。现关闭 issue,若有任何问题,可评论回复。

@yangly120 yangly120 mentioned this issue Nov 16, 2022
@yangly120
Copy link

少一个给缓存Vue实例添加cachedInstance.$router.apps = [...cachedInstance.catchRoute.apps],离开子应用再跳转回到子应用别的路由会发生页面不刷新的问题,cachedInstance.$router.push方法无效

@yangly120
Copy link

router.onReady(() => {
const { path, query } = router.currentRoute;
const { path: oldPath } = cachedInstance.$router.currentRoute;
// 当前路由和上一次卸载时不一致,则切换至新路由
if (path !== oldPath) {
cachedInstance.$router.push({path, query});
}
});
有参数的跳转也要加上参数

@duowb
Copy link

duowb commented May 6, 2023

我的Vue子应用可以实现Tab切换并保持页面状态,代码如下:

let instance = null;
let router = null;

function render() {
  // 这里必须要new一个新的路由实例,否则无法响应URL的变化。
  router = new VueRouter({
    mode: "hash",
    base: process.env.BASE_URL,
    routes
  });
  if (
    window.__POWERED_BY_QIANKUN__ &&
    window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__
  ) {
    const cachedInstance = window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__;

    // 从最初的Vue实例上获得_vnode
    const cachedNode =
      // (cachedInstance.cachedInstance && cachedInstance.cachedInstance._vnode) ||
      cachedInstance._vnode;

    // 让当前路由在最初的Vue实例上可用
    router.apps.push(...cachedInstance.$router.apps);
    // keepAlive可用
    cachedNode.data.keepAlive = true;

    instance = new Vue({
      router,
      store,
      render: () => cachedNode
    });

    // 缓存最初的Vue实例
    instance.cachedInstance = cachedInstance;

    const { path } = router.currentRoute;
    const { path: oldPath } = cachedInstance.$router.currentRoute;
    // 当前路由和上一次卸载时不一致,则切换至新路由
    if (path !== oldPath) {
      cachedInstance.$router.push(path);
    }
    instance.$mount("#app");
  } else {
    // 正常实例化
    instance = new Vue({
      router,
      store,
      render: h => h(App)
    }).$mount("#app");
  }
}

if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

export async function bootstrap() {
  console.log("[vue] vue app bootstraped");
}

export async function mount(props) {
  console.log("[vue] props from main framework", props);
  render(props);
}

export async function unmount() {
  console.log("[vue] vue app unmount");
  window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__ =
    instance.cachedInstance || instance;
  // 不销毁实例
  // instance.$destroy();
  // instance = null;
  router = null;
}

请问一下,这样第一次是不会触发activated,生命周期,有什么办法解决吗?

@YYL1999
Copy link

YYL1999 commented May 11, 2023

手动load微前端,然后tabs 直接 缓存微前端的dom,tab关闭手动卸载。

这样页面会闪的 会去加载css

@niubishuangjiang
Copy link

遍历销毁本想着最后迫不得已才做,谢谢大佬的思路,受宠若惊,没想到回复这么快。
vue内部应该也是这么去做的,只不过现在需要手动干预了,你这边实现了的话 可以分享一下代码,我目前还没开始做(只有一个示例代码)

// cachedInstance.catchRoute.catchComponent.forEach(name => {
  //   window.func.findComponents(name, cachedInstance.catchRoute).$destroy()
  // })

大佬能贴完整代码吗

@YYL1999
Copy link

YYL1999 commented Jun 1, 2023

遍历销毁本想着最后迫不得已才做,谢谢大佬的思路,受宠若惊,没想到回复这么快。
vue内部应该也是这么去做的,只不过现在需要手动干预了,你这边实现了的话 可以分享一下代码,我目前还没开始做(只有一个示例代码)

// cachedInstance.catchRoute.catchComponent.forEach(name => {
  //   window.func.findComponents(name, cachedInstance.catchRoute).$destroy()
  // })

大佬能贴完整代码吗

我也实现了 主体思想是主应用设置一个全局map,用来存储微应用实例,然后路由拦截,将路由作为key,微应用实例作为value,然后每次新增tab就存起来,移除tab就删掉当前微应用实例并销毁。

@niubishuangjiang
Copy link

遍历销毁本想着最后迫不得已才做,谢谢大佬的思路,受宠若惊,没想到回复这么快。
vue内部应该也是这么去做的,只不过现在需要手动干预了,你这边实现了的话 可以分享一下代码,我目前还没开始做(只有一个示例代码)

// cachedInstance.catchRoute.catchComponent.forEach(name => {
  //   window.func.findComponents(name, cachedInstance.catchRoute).$destroy()
  // })

大佬能贴完整代码吗

我也实现了 主体思想是主应用设置一个全局map,用来存储微应用实例,然后路由拦截,将路由作为key,微应用实例作为value,然后每次新增tab就存起来,移除tab就删掉当前微应用实例并销毁。

大佬可不可以贴点代码,我销毁不了离开的页面缓存

@chenxukf103
Copy link

chenxukf103 commented Sep 22, 2023

采用[Liaoct]的方案实现多页签,但是遇到了一个很奇怪的问题,主应用有个路由c,子应用有两个路由a,b,a路由跳转到b,切换到路由c,再回到b页签,再关闭b页签回到a,再从a跳转到b,这个时候会发现this.$router.push({name:'b',params:{'a':'1'}})的params丢失了,获取不到,从a跳转去其他路由的params都处于丢失的状态,大佬们,有遇到这个问题吗?Vue版本是2
@Liaoct

@wash1983
Copy link

let router = null;
// 乾坤框架配置开始
let instance = null;
//缓存的示例, tab页未全部关闭前,保持状态使用
let cachedData = null;

//挂载实例
function render(props = {}) {

const {
    container
} = props;

router = routes;

if (cachedData != null) {
    //这个必须设置, 不然无法监听路由变化
    router.apps = cachedData.cachedRouter;
    instance = new Vue({
        router,
        store,
        render: () => cachedData.cachedVNode
    });
    // const { path } = router.currentRoute;
    //     const { path: oldPath } = cachedInstance.$router.currentRoute;
    //     // 当前路由和上一次卸载时不一致,则切换至新路由
    //     if (path !== oldPath) {
    //       cachedInstance.$router.push(path);
    //     }
    instance.$mount(container ? container.querySelector('#app') : '#app');
} else {
    // 正常实例化    
    instance = new Vue({
        router,
        store,
        render: h => h(App)
    }).$mount(container ? container.querySelector('#app') : '#app');
}

}

//判断当前运行环境是独立运行的还是在父应用里面进行运行,配置全局的公共资源路径
// qiankun
if (window.POWERED_BY_QIANKUN) {
// eslint-disable-next-line no-undef
webpack_public_path = window.INJECTED_PUBLIC_PATH_BY_QIANKUN;
}
//如果是独立运行window.POWERED_BY_QIANKUN=undefined
if (!window.POWERED_BY_QIANKUN) {
render();
}
//最后暴露的三个方法是固定的,加载渲染以及销毁
export async function bootstrap() {
console.log('[vue] vue app bootstraped');
}

export async function mount(props) {
// console.log('[vue] props from main framework', props);
// initGState(props);
render(props);
}

export async function unmount() {

console.log('[vue] vue app unmount');
if (ifCache()) {
    const a = instance.$router.apps.splice(0,1)
    cachedData = {
        cachedVNode: instance._vnode,
        //必须实拷贝不能直接赋值, 不然后面的destroy 会置空
        cachedRouter: [...a],
    }
    //这个必须设置, 不然无法保持状态
    cachedData.cachedVNode.data.keepAlive = true;
} else {
    destroyCache()
}
instance.$destroy();
//这个不能写, 不然会是空白页面
//instance.$el.innerHTML = '';
instance = null;
// router = null;

}
function onTagsChange(tags){
if(!ifCache()){
destroyCache();
}

}
bus.$on("tagsChange",onTagsChange)
/**

  • 是否需要缓存, 如果有打开的tab就缓存,不然就真实销毁
    /
    function ifCache( ) {
    let tags=gtags.getTags()
    if (!Array.isArray(tags)) {
    return false
    }
    let hastag = tags.some(e => {
    return e.app == packageName;
    })
    return hastag;
    }
    /
    *
  • 销毁缓存的应用实例
    */
    function destroyCache() {
    if (cachedData != null && cachedData.cachedVNode != null) {
    if (cachedData.cachedVNode.child) {
    cachedData.cachedVNode.child.$destroy();
    }
    cachedData = null;
    }
    }

@kuyoru-kamikisho
Copy link

参考代码地址: https://github.com/kuaifengle/qiankun-vue3-tabsPage 网页演示网址: https://kuaifengle.github.io/qiankun-vue3-tabsPage-demo

看了您的代码,得出您是这样实现的:
点击菜单--->
跳转路由到path
路由的变化会被主应用监听到,子应用是否会被激活完全靠的导航守卫
对于主应用来说,子应用path对应的路由仅仅是一个空元素
子应用path是否被缓存取决于其是否是"iframe<主应用>"属性
根据路由,如果对应的path是子应用,则创建微应用,否则直接展示
创建微应用时,未处于活跃状态的将被隐藏
点击叉号完全销毁子应用,不会缓存状态(天然符合预期)
点击tab和点击菜单都只修改路由
----
如果子应用已经加载过,则不再执行二次loadMicroApp()
反之则执行loadMicroApp()并储存返回值
在加载子应用时,会将父应用的整个路由对象、状态机的部分信息传递给子应用
已经加载过的子应用完全是靠的“隐藏”模式实现的,即不让子应用unmount,完全采用隐藏的模式实现状态保活

    隐藏代码:
    <div v-show="!$route.name" v-loading="appLoading" element-loading-text="加载页面中, 请稍后...">
              <div
                v-for="item in tabsList"
                v-show="item.appName === activeTab.appName"
                :key="item.appName"
                :id="item.id"
              >
              </div>
    </div>

参照下面的评论里我实现了vue2子应用unmount也能保活,但是vue3的无法保活
想执行unmount是因为如果不卸载子应用的样式我怕会对主应用造成干扰
有大佬知道vue3怎么unmount也依然能实现状态保活吗?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests