When you're making a layout with tabs, you'll quickly come to code like this

<router-view v-slot="{ Component }">
  <transition>
    <keep-alive>
      <component :is="Component" />
    </keep-alive>
  </transition>
</router-view>

KeepAlive is used to keep the current route component alive. So as to speak, it keeps its state when navigating away.

Later you put more and more pages in one router. You'll have lots of cached components. Now we can set max so as to let it use LRU algorithm to prune lest used components. Everything works fine now.

Then designer asks you to make a tab over the top of the page to keep track of visited pages and have delete button to removed tabs. Now you have a problem there's no api to remove specific cache for keep-alive component. After a while's digging in vue's source code I found there's a way.

That is to use the exclude prop. KeepAlive's watching this prop and run pruning code if this prop changes.

Another thing to watch out is include/exclude prune cache using components name not keys. When you have a few tabs of the same component. It's going to be a problem. A workaround for this is to use dynamic component. Give each tab a unique name, eg. url, uuid.

The final code roughly looks like this:

const route = useRoute();
const tabStore = useTabStore();
const excludeComponents = reactive([]);
const componentCache = {};

tabStore.$onAction(({ name, store, args, after }) => {
    if (name == 'closeTab') {
        const key = makeKey(args.tab);
        after(() => {
            excludeComponents.push(key);
            delete componenntCache[key];
            nextTick(() => {
                excludeComponents.pop();
            });
        });
    }
});

const component = computed(() => {
    const key = makeKey(route);
    if (!(key in componentCache)) {
        componentCache[key] = {
            name: key,
            render() {
                return h(route.component);
            },
        };
    }
    return componentCache[key];
});