Skip to content

When Vue has some other global mixins, 'mixins' helper could be dangerous #291

Closed
@hikerpig

Description

@hikerpig

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

src/mixin.js

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
    }
})

src/index.js

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

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions