I'm using a Popup style UI component in a Nuxt.js base project. This is used by many pages and routes, so I declared and initiated as global component plugin when the app starts, like below:
// nuxt.config.js
plugins: [
{ src: '~/plugins/popup/index.js', mode: 'client' },
],
// plugins/toast/index.js
import Vue from 'vue';
import PopupComponent from './Popup.vue';
const PopupConstructor = Vue.extend(PopupComponent);
export default () => {
Vue.use({
install: () => {
let _popup = new PopupConstructor();
window.popup = Vue.prototype.popup = {
appear: _popup.appear,
disappear: _popup.disappear
};
_popup.vm = _popup.$mount();
_popup.dom = _popup.vm.$el;
document.body.appendChild(_popup.dom);
}
});
};
// Popup.vue
// some edit applied for the sake of simplicity
<template>
<div
class="popup"
:class="{
'--error': error,
'--visible': visible
}"
ref="popup"
>
<div class="content" ref="content">
<div class="title">{{title}}</div>
<div class="text">{{detail}}</div>
</div>
</div>
</template>
import gsap from 'gsap';
export default {
data: function () {
return {
visible: false,
title: '',
detail: '',
timer: 3000,
timeout: null,
animationTimeout: null,
};
},
created() {
},
mounted() {
this.$_appear = null;
this.$_disappear = null;
},
beforeDestroy() {
this.$_appear.kill();
this.$_appear = null;
this.$_disappear.kill();
this.$_disappear = null;
},
appear({ title, detail }) {
if (this.visible) {
this.clearTimeout();
}
this.visible = true;
this.$_appear.kill();
this.$_disappear.kill();
this.title = title;
this.detail = detail;
this.$_showAni = gsap.to(this.$refs.popup, 0.5, {
css: {
top: '100px',
opacity: 1
},
onComplete: () => {
this.$_appear = null;
}
});
this.timeout = window.setTimeout(() => {
this.disappear();
}, this.timer);
},
disappear() {
this.clearTimeout();
this.$_disappear.kill();
this.$_disappear = gsap.to(this.$refs.popup, 0.5, {
css: {
top: '100px',
opacity: 0
},
onComplete: () => {
this.$_disappear = null;
this.visible = false;
}
});
},
clearTimeout() {
if (this.timeout) {
window.clearTimeout(this.timeout);
this.timeout = null;
}
}
}
As you see, by this code the Popup
vue component's methods(appear
, disappear
) will be accessible through window.popup
, and the component itself will be created, mounted, attached on document
.
This works just fine, but the problem is it seems this leads to memory leak. As I profile the memory allocation timeline using Chrome devtool, from some point of time memory allocated with window
causes retained(dangling?; could be GC-ed but left due to reference using?) memory.
Is the usage of plugin like above okay? If not, to get the same utility while preventing memory leak, which part should be corrected?
EDIT:
I added the simple version implementation code for Popup
which uses GSAP library for an animation. It uses the animation for appear and disappear sequentially.