Closed
Description
I have added a minimal demo in test/test.ts
it('mixins should be safe with VueContructor and global mixins', function () {
let counter = 0
/**
* dangerous global mixin
*/
Vue.mixin({
created() {
counter++
}
})
@Component
class MixinA extends Vue {
}
@Component
class MixinB extends Vue {
}
@Component
class MyComp extends mixins(MixinA, MixinB) {
test() {}
}
const vm = new MyComp()
vm.test() // just to avoid 'noUnusedLocals' error
expect(counter).to.equal(1) // error, actual 3
})
Global mixin is very common in Vue plugins and real-world projects. With current implementation of 'mixins' helper( or of Vue.extend({ mixins: VueConstructor<any>[] })
), there might be some subtle unexpected results.
For example, I have found a vue-i18n caused memory leak in work project
Vue.mixin({
beforeCreate (): void {
// .... in short ...
const options: any = this.$options
options.i18n = options.i18n || (options.__i18n ? {} : null)
this._i18n = options.i18n || (options.__i18n ? {} : null)
this._i18n.subscribeDataChanging(this)
},
beforeDestroy (): void {
if (!this._i18n) { return }
if (this._subscribing) {
// if two 'beforeCreate' hooks has been added to options, there will be two vm pushed to listener array by '_i18n.subscribeDataChanging'
// but here, only one '_i18n._i18n.unsubscribeDataChanging' will be called
// so there is a leak
this._i18n.unsubscribeDataChanging(this)
}
// ...
this._i18n = null
}
})
class VueI18n {
// ...
subscribeDataChanging (vm: any): void {
this._dataListeners.push(vm)
}
unsubscribeDataChanging (vm: any): void {
// will only remove the first 'vm' in 'this._dataListeners'
remove(this._dataListeners, vm)
}
}
Metadata
Metadata
Assignees
Labels
No labels