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

Add API to manually remove/unmount component in <keep-alive> cache #2077

Closed
AgileInteractive opened this issue Sep 9, 2020 · 7 comments
Closed
Labels

Comments

@AgileInteractive
Copy link

What problem does this feature solve?

When using vue-router to navigate in a larger SPA, keep-alive is a very nice way to keep views/components that are frequently visited in the cache. When the instance of the component are cached, one avoids time and energy to reload it into memory (especially GPU related assets, which otherwise would cause unnecessary loading delay for the user).

In other words, keep-alive helps building a snappy and smooth experience for the user.

However, this also means that the components that have been visited are adding up in the memory, and can also consume some CPU even if they are never to be visited again by the user.

One can argue that the "max" property can limit this, but, I think there can be many different reasons to control this manually instead. In our specific case, we would like to control this for performance reasons (based on frequency of times the component is visited, time since last visit, how time consuming is the component to load, memory consumption of the component, etc.).

Right now we have managed to fix this with a fork, where we expose the function pruneCacheEntry(key: CacheKey) in KeepAlive.ts, but it would have been much better if this was a part vue itself.

What does the proposed API look like?

<keep-alive ref="aliveKeeper">
</keep-alive>

...

this.$refs.aliveKeeper.removeFromCache(myVueComponent);
@posva
Copy link
Member

posva commented Sep 9, 2020

This has been discussed multiple times: vuejs/vue#6259 vuejs/vue#8028 vuejs/vue#10487.

This is probably worth going through an RFC by comparing existing solutions with include, exclude and max

@AgileInteractive
Copy link
Author

If include/exclude could match some unique feature of the instance (type or the instance itself), it might be sufficient, but I am not sure if that is a better solution than just explicitly remove an instance of a vue component from the cache.

@HcySunYang
Copy link
Member

Closed, tracking here vuejs/rfcs#284

@yuanjinyong
Copy link

yuanjinyong commented Jul 27, 2021

I used the following code, it seems ok:

      <router-view v-slot="{Component, route}">
        <keep-alive ref="keepAlive"><component :key="route.path" :is="Component" /></keep-alive>
      </router-view>
    onCloseTab(targetName) {
      const route = this.tabRoutes.find(route => targetName === route.params.f_id)
      this.$store.dispatch('removeTab', targetName)

      // 删除组件在keepalive里的cache,参考 pruneCacheEntry 这个方法的逻辑
      const key = route.path
      const keepAliveInstance = this.$refs.keepAlive.$
      const cache = keepAliveInstance.__v_cache
      const vnode = cache.get(key)
      if (vnode) {
        let shapeFlag = vnode.shapeFlag
        if (shapeFlag & 256 /* COMPONENT_SHOULD_KEEP_ALIVE */) {
          shapeFlag -= 256 /* COMPONENT_SHOULD_KEEP_ALIVE */
        }
        if (shapeFlag & 512 /* COMPONENT_KEPT_ALIVE */) {
          shapeFlag -= 512 /* COMPONENT_KEPT_ALIVE */
        }
        vnode.shapeFlag = shapeFlag

        const renderer = keepAliveInstance.ctx.renderer
        renderer.um(vnode, keepAliveInstance, keepAliveInstance.suspense)

        cache.delete(key)
      }
    }

@tony-gm
Copy link

tony-gm commented Mar 4, 2022

@yuanjinyong I did the similar trying before, but the __v_cache only exposed in develop mode, can not used in production.

@Fennec-hub
Copy link

Fennec-hub commented Mar 27, 2022

Any update, hack or work around?
I'm trying to achieve something like this example, Where you can dynamically add, remove or change tabs components and there is no way to clear the removed components cache.

Thanks, and by the way, Vue3 is just ✨ awesome ✨

@charonchan1991
Copy link

charonchan1991 commented Jul 3, 2023

I encountered the same issue when trying to implement tabs that share a same component. When a tab is closed by user, I would like to delete the cache of that particular instance only. Here is what I think would be the most elegant way to tackle this in Vue3.

Since KeepAlive takes include/exclude that accepts component names only, one of the most obvious solution to this problem is to wrap the shared component into an HOC that gives every instance a unique name. Here is my implementation:

const createUniqueComponent = (component) => {
  return markRaw(
    defineComponent({
      name: createUniqueId(), // any unique string generation method of your choice
      components: {
        uc: component
      },
      render() {
        return h(this.$.components.uc, null, this.$slots)
      }
    })
  )
}

Then, use it where you need to create the component instance, for example, in the router or a store. In my case, I am dynamically importing the component, so the code would look something like this:

tabs.push({
  ...
  page: createUniqueComponent(defineAsyncComponent(() => import('@/views/SomeTabPage')))
})

Finally, generate a computed property for use as the include prop for KeepAlive.

const keepAliveIncludes = computed(() => tabs.map((tab) => tab.page.name))

Now bind it to the include prop of your KeepAlive and enjoy the magic:

<KeepAlive :include="keepAliveIncludes">
  <component :is="tab.page">
</KeepAlive>

In the above example, when you remove an item from the tabs array (user closes the tab), the cache of that particular instance will be removed too, whilst the remaining instances that use the same component will stay and keep their states. You can confirm this in your Vue DevTools.

@github-actions github-actions bot locked and limited conversation to collaborators Sep 8, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

8 participants