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

Memory leak in vue-i18n when v-media-query is present #498

Closed
i4i4n opened this issue Jan 7, 2019 · 5 comments
Closed

Memory leak in vue-i18n when v-media-query is present #498

i4i4n opened this issue Jan 7, 2019 · 5 comments

Comments

@i4i4n
Copy link

i4i4n commented Jan 7, 2019

vue & vue-i18n & v-media-query

2.5.21, 8.6.0, 1.0.3

Issue

vue-i18n does not handle memory properly in presence of the v-media-query library.

It seems that when components that load i18n texts are deleted, the loaded texts are not properly wiped from memory.
As a result, the app usage more and more memory (especially when components are toggled by v-ifs) and the slower the app gets over time.

Steps to reproduce

I haven't used JSFiddle or a similar tool because the use of an iframe masks Vue's memory usage.

main.js

import Vue from 'vue'
import VueI18n from 'vue-i18n'
import VueMediaQuery from 'v-media-query'

Vue.use(VueI18n)
Vue.use(VueMediaQuery)

const i18n = new VueI18n({locale: 'en'})

const ComponentWithI18n = {
  i18n: {
    messages: {
      en: {
        'text': 'translation'
      }
    }
  },
  template: `<div>{{ $t('text') }}</div>`,
}

const App = {
  template: `<div>
    <button @click="show = !show">Toggle</button>
    <div v-if="show">
      <component-with-i18n v-for="i in 10" :key="i" />
    </div>
  </div>`,

  data () {
    return {
      show: true,
    }
  },

  components: {
    ComponentWithI18n
  },
}

new Vue({
  i18n,
  render: h => h(App),
}).$mount('#app')

index.html

<div id="app"></div>

Watch the memory usage increase

  • serve this app in a dev environment (I used vue-cli 3.x)
  • in Chrome devtools, use the Memory tab to take a heap snapshot
  • search Vue in the snapshot's summary and write the number of Vue constructors down
  • click on the toggle button to hide then show the list once
  • take another snapshot and write the number of Vue constructors down again: it has doubled
  • hide/show the list once more, take another snapshot: Vue constructors has increased again

Watch the memory usage go back to normal

  • comment/remove the v-media-query library import and use from the code
  • serve this app in a dev environment (I used vue-cli 3.x)
  • take a snapshot, hide/show the list, take another snapshot: Vue constructors remains stable the same

What is expected?

Component loaded translations are removed from memory when the component is destroyed whether v-media-query is present or not.

What is actually happening?

Component loaded translations are not removed from memory when a component is destroyed when v-media-query is present.

@kazupon
Copy link
Owner

kazupon commented Jan 9, 2019

It seem same as vuejs/vue-class-component#291

@kazupon
Copy link
Owner

kazupon commented Jan 9, 2019

Wait until Vue 2.6 is released.

@i4i4n i4i4n closed this as completed Jan 9, 2019
@aiankile
Copy link

@kazupon

The actual issue is: A number of Vue plugins (including quite reknown ones), including v-media-query, keep track of all existing Vue instances / components via globally injected beforeCreate hooks, basically saving references to "this" of each created instance. Obviously, this functionality must rely on correct destruction of all instances, removing saved reference in beforeDestroy.

"Normal" VueComponent instances are destroyed automatically by Vue. For manually created instances (not bound to a parent), however, $destroy has to be called manually.

In vue-i18n component-based variant, a new Vue instance is created as this._i18n._vm in each component with a local i18n option. While watchers are being correctly unregistered in beforeDestroy and the reference this._i18n is being cleared, the instance itself doesn't get destroyed via $destroy. Meaning, those global plugins are keeping a reference to it.

A simple solution would be a call to this._i18n._vm.$destroy(); just before this._i18n = null;. This is, however, only possible if _i18n was created locally, else we're destroying root or parent i18n vm.

As of now, existence of _i18nWatcher can be used as flag for "it's our local _i18n copy", leading us to the following crude fix:

var doDestroy = !!this._i18nWatcher
// watcher deletion
if (doDestroy) {
this._i18n._vm.$destroy();
}

Though there is surely a more elegant solution.

@aiankile
Copy link

@kazupon Should I open a new issue with the detailed description? Though reopening this one might make more sense as my use case is exactly the same (v-media-query) and the cause isn't exactly covered by the linked issue.

@tvld
Copy link

tvld commented Feb 28, 2020

I think I have same issue with Vuetify and Vue-i18n although in Nuxt. Memory goes out of bound after a while. What's the status on this one? Still solved with 2.6 ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants