Frontend Development 17 min read

Multi-Tab Caching Strategies for Micro‑Frontend Applications Using qiankun

The Vivo Internet Front‑End team explains how to implement multi‑tab functionality and efficient caching in a qiankun‑based micro‑frontend architecture, comparing CSS hide/show and component‑level keep‑alive approaches, then presenting two strategies—simultaneous mounting versus app‑level vnode caching—choosing the latter for reduced memory usage and detailing its Vue‑specific implementation, communication handling, and future Vue 3 migration.

vivo Internet Technology
vivo Internet Technology
vivo Internet Technology
Multi-Tab Caching Strategies for Micro‑Frontend Applications Using qiankun

This article, authored by the Vivo Internet Front‑End team, reviews how to implement multi‑tab functionality and caching in a micro‑frontend architecture based on the open‑source framework qiankun . It first explains what multi‑tab means from a product perspective and compares two common implementation methods for single‑page applications (SPA): using CSS display:none to hide/show pages, and serializing component state (similar to Vue’s keep-alive ) to cache DOM nodes.

It then discusses the differences when the SPA is transformed into a micro‑frontend using qiankun. In a qiankun setup the application is split into a base app and several sub‑apps, each running in an isolated sandbox. Because each sub‑app has its own keep-alive , the base app must manage caching for all sub‑apps.

1. Two solution ideas

After researching GitHub issues and articles, two implementation ideas were identified:

Keep all sub‑apps mounted simultaneously and control visibility with v-show / display:none . Load sub‑apps via loadMicroApp and unmount them manually when a tab is closed.

Register sub‑apps with registerMicroApps so that only one sub‑app is active at a time. Each sub‑app caches its own vnode (similar to Vue’s keep-alive ) and restores it on re‑entry.

Both approaches are illustrated with code examples.

created() {
  // 动态删除缓存实例监听
  this.cache = Object.create(null);
  breadCompBus.$on('removeTabByKey', this.removeCacheByKey);
  breadCompBus.$on('removeTabByKeys', (data) => {
    data.forEach((item) => {
      this.removeCacheByKey(item);
    });
  });
}
// 传入`vue-keep-alive`的自定义方法
function updateComponentsKey(key, name, vnode) {
  const match = this.$route.matched[1];
  if (match && match.meta.multiNodeKey) {
    vnode.key = match.meta.multiNodeKey(key, this.$route);
    return vnode.key;
  }
  return key;
}

2. Evaluation and final choice

Solution 1 is fast because sub‑apps are never unmounted, but it leads to excessive DOM nodes and event listeners, causing memory waste and possible page lag. Solution 2 adds a small extra patch step when re‑rendering, but it reduces DOM count and frees memory when inactive tabs are unmounted. Considering the project’s heavy editor and data‑display workload, the team chose Solution 2.

3. Detailed implementation of Solution 2

The core idea is to cache the vnode of each sub‑app at the application level, not just the component level. The following snippets show how the keep‑alive core is adapted, how the base app provides an unmountCache method, and how the sub‑app’s unmount calls it.

// keep-alive核心代码
render () {
  const slot = this.$slots.default;
  const vnode = getFirstComponentChild(slot);
  const componentOptions = vnode && vnode.componentOptions;
  if (componentOptions) {
    if (cache[key]) {
      vnode.componentInstance = cache[key].componentInstance;
      remove(keys, key);
      keys.push(key);
    } else {
      this.vnodeToCache = vnode;
      this.keyToCache = key;
    }
    vnode.data.keepAlive = true;
  }
  return vnode || (slot && slot[0]);
}
mounted() { this.cacheVNode(); }
updated() { this.cacheVNode(); }
// 父应用提供unmountCache方法
function unmountCache() {
  const needCached = this.instance?.cachedInstance || this.instance;
  const cachedInstance = {};
  cachedInstance._vnode = needCached._vnode;
  if (!cachedInstance._vnode.data.keepAlive) cachedInstance._vnode.data.keepAlive = true;
  loadedApplicationMap[this.cacheKey] = cachedInstance;
  this.instance.$destroy();
  this.instance = null;
}
export async function unmount() {
  console.log('[vue] system app unmount');
  mainService.unmountCache();
}

When a new sub‑app is created, the cached vnode (if any) is used as the render function:

// 创建子应用实例,有缓存的vnode则使用缓存的vnode
function newVueInstance(cachedNode) {
  const config = {
    router: this.router,
    store: this.store,
    render: cachedNode ? () => cachedNode : instance.render,
  };
  return new Vue(config);
}
this.instance = newVueInstance(cachedNode);
this.instance.$mount('#app');

Additional challenges such as vue‑router state loss and parent‑child communication are addressed by re‑initialising the router on each mount and using qiankun’s setGlobalState together with custom events for communication between the base app and multiple sub‑apps.

// 自定义事件发布
const evt = new CustomEvent('microServiceEvent', {
  detail: { action: { name: action, data }, basePath },
});
document.dispatchEvent(evt);
// 自定义事件监听
document.addEventListener('microServiceEvent', this.listener);

Cache management is emphasized to avoid memory leaks: application‑level caches store vnode, router, and other state; page‑level caches are cleared when a tab is closed.

The article concludes that the presented approach offers an alternative to the more common component‑level keep‑alive solution, providing a way to cache entire sub‑applications while keeping memory usage under control. It also notes that the current implementation only supports Vue, and a future migration to Vue 3 is planned.

frontendcachingmicro-frontendVueqiankunmulti-tab
vivo Internet Technology
Written by

vivo Internet Technology

Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.