'How to destroy/unmount vue.js 3 components?
I have a fairly large vue.js 2 application that has a dynamic tabs mechanism. Users can interact with the application opening and closing tabs, each tab represents a route. To achieve this I'm using vue router and keep alive, like the example bellow.
<template>
<div id="app">
<keep-alive>
<router-view :key="routerViewKey"/>
</keep-alive>
</div>
</template>
When users click the close tab button, the $destroy
function is called to remove its component from the cache. I'm migrating this application from vue 2 to vue 3, however, reading the breaking changes documentation for vue.js 3 we can see this:
Removed APIs
$destroy
instance method. Users should no longer manually manage the lifecycle of individual Vue components.
I didn't find any alternatives so far, so how can I programmatically destroy/unmount a cached component inside keep-alive in vue.js 3?
Edit 1 (Apr/22): So far it's still impossible to achieve what $destroy
did on vue.js 2 in vue.js 3. There is currently a RFC to solve this issue (https://github.com/vuejs/rfcs/discussions/283), but unfortunately it's opened for more than a year without any feedback.
Solution 1:[1]
The destroy hook has been replaced with unmounted. I guess you can import unmounted from the composition APIs.
Solution 2:[2]
You can find the unmount command here: https://v3.vuejs.org/api/application-api.html#unmount
Unfortunately, if you want to do it inside your app, the documentation doesn't have any way to do it.
However, after analyzing the object, I found a way to do it. You can achieve it by using this: this.$.appContext.app.unmount();
I'm not a huge fan of this solution, given that it can no longer work in a future version, but it works well on my project.
EDIT:
Another way is to extend the Vue
object according to this: https://github.com/vuejs/vue-next/issues/1802 and https://github.com/pearofducks/mount-vue-component
I have slightly improved the function:
function mount(component, { props, children, element, app } = {}) {
let el = element
let vNode = createVNode(component, props, children)
vNode.destroy = () => {
if (el) render(null, el)
el = null
vNode = null
}
if (app?._context) vNode.appContext = app._context
if (el) render(vNode, el)
else if (typeof document !== 'undefined' ) render(vNode, el = document.createElement('div'))
const destroy = () => {
if (el) render(null, el)
el = null
vNode = null
}
return { vNode, destroy, el }
}
You can now track which component you have as children, destroy it from the parent AND the children by using this: this.$.vnode.destroy();
However, the new official way seems to use createApp
now.
Solution 3:[3]
Try with v-if
<template>
<div
v-if="!close"
:class="`z-50 absolute py-4 mx-4 ${position}`"
data-testid="notification"
>
...
<div v-if="enableClose" class="cursor-pointer absolute right-1 top-3 text-gray-500 text-2xl" @click="onClose">
×
</div>
</Flash>
</div>
</template>
<script lang="ts">
import {defineComponent, render} from "vue";
import Flash from "@/core/shared/components/Layout/Flash.vue";
export default defineComponent({
name: "Toast",
components: {
Flash,
},
props: {
...
enableClose: {
type: Boolean,
default: true,
},
},
computed: {
...
},
data() {
return {
close: false
};
},
methods: {
onClose() {
this.close = true;
},
},
});
</script>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Cristiano Soleti |
Solution 2 | |
Solution 3 | PaKo Anguiano |