Describe the bug
When using apollo like props in a component, after we remove this component, the number of DOM nodes does not decrease. Because of this, memory is constantly growing.
If you do not use Apollo in a component, then after its destruction, the DOM nodes disappear from memory using the garbage collector and memory usage drops.
cache.evict() and cache.gc() delete data from the cache, but does not help solve the problem.
To Reproduce
I made a repository with an example and recorded a short video on how to reproduce this problem.
Expected behavior
DOM nodes should be removed from memory when a component is removed.
Versions
"@apollo/client": "^3.1.4",
"graphql": "^15.3.0",
"graphql-tag": "^2.11.0",
"vue": "^2.6.11",
"vue-apollo": "^3.0.4"
Seeing this as well, the only way to help alleviate the issue has been to extend the normal Vue destroy hook and manually prep everything for garbage collection.
Seeing this as well, the only way to help alleviate the issue has been to extend the normal Vue
destroyhook and manually prep everything for garbage collection.
Can you show an example for this, please?
@frederikhors https://github.com/edbond88/vue-apollo-memory-leak
@frederikhors We've been experimenting with some variation of this:
const blockedProps = [
'$data',
'$listeners',
'$attrs',
'$props'
]
Vue.mixin({
destroyed() {
const prepForGC = () => {
try {
this.$el?.remove()
this.elm = null
this.$el = null
this.parent = null
this.$parent = null
this.data = null
// this.$data = null // Unsetting this throws internal Vue errors
// this.options = null // Unsetting this throws internal Vue errors
this.$options = null
this.$vnode = null
this.listeners = null
// this.$listeners = null // Unsetting this throws internal Vue errors
this._vnode = null
this._watcher = null
this._watchers = null
this._computedWatchers = null
this.$slots = null
this.slots = null
this.$scopedSlots = null
this.scopedSlots = null
this.$children = null
this.children = null
this.store = null
this.$store = null
// $apollo has no setter so we clear props instead
if (this.$apollo) {
this.$apollo.vm = null
}
Object.keys(this).forEach(key => {
try {
!blockedProps.includes(key) &&
!(key in this[key]?.$props) &&
(this[key] = null)
} catch {
/* */
}
})
} catch (e) {
// eslint-disable-next-line no-console
console.error('Error in destroyed garbage collector mixin', e)
}
}
// If the element has already been removed from the DOM
// we run the GC prep method immediately
if (!document.body.contains(this.$el)) {
prepForGC()
return
}
// Otherwise we can assume there's some sort of transition
// on the component and we wait for it to complete
// Note: this is obviously rough but Observer methods
// were performing poorly.
setTimeout(prepForGC, 1500)
}
})
I can't reproduce with this forked with updated dependencies and fixed build: https://github.com/Akryum/vue-apollo-memory-leak