Skip to content

Commit

Permalink
avoid duplicate lifecycle hooks during constructor resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Feb 24, 2017
1 parent 673acec commit 7fa8fa7
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 14 deletions.
33 changes: 25 additions & 8 deletions src/core/instance/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ function initInternalComponent (vm: Component, options: InternalComponentOptions
export function resolveConstructorOptions (Ctor: Class<Component>) {
let options = Ctor.options
if (Ctor.super) {
const superOptions = Ctor.super.options
const superOptions = resolveConstructorOptions(Ctor.super)
const cachedSuperOptions = Ctor.superOptions
if (superOptions !== cachedSuperOptions) {
// super option changed,
Expand All @@ -108,14 +108,31 @@ export function resolveConstructorOptions (Ctor: Class<Component>) {
}

function resolveModifiedOptions (Ctor: Class<Component>): ?Object {
let res
const options = Ctor.options
let modified
const latest = Ctor.options
const sealed = Ctor.sealedOptions
for (const key in options) {
if (sealed[key] !== options[key]) {
if (!res) res = {}
res[key] = options[key]
for (const key in latest) {
if (latest[key] !== sealed[key]) {
if (!modified) modified = {}
modified[key] = dedupe(latest[key], sealed[key])
}
}
return res
return modified
}

function dedupe (latest, sealed) {
// compare latest and sealed to ensure lifecycle hooks won't be duplicated
// between merges
if (Array.isArray(latest)) {
const res = []
sealed = Array.isArray(sealed) ? sealed : [sealed]
for (let i = 0; i < latest.length; i++) {
if (sealed.indexOf(latest[i]) < 0) {
res.push(latest[i])
}
}
return res
} else {
return latest
}
}
22 changes: 16 additions & 6 deletions test/unit/features/global-api/mixin.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,31 +87,41 @@ describe('Global API: mixin', () => {

// #4976
it('should not drop late-attached custom options on existing constructors', () => {
const Test = Vue.extend({})
const baseSpy = jasmine.createSpy('base')
const Base = Vue.extend({
beforeCreate: baseSpy
})

const Test = Base.extend({})

// Inject options later
// vue-loader and vue-hot-reload-api are doing like this
Test.options.computed = {
$style: () => 123
}

const spy = jasmine.createSpy('mixin')
Test.options.beforeCreate = [spy]
const spy = jasmine.createSpy('late attached')
Test.options.beforeCreate = Test.options.beforeCreate.concat(spy)

// Update super constructor's options
Vue.mixin({})
const mixinSpy = jasmine.createSpy('mixin')
Vue.mixin({
beforeCreate: mixinSpy
})

// mount the component
const vm = new Test({
template: '<div>{{ $style }}</div>'
}).$mount()

expect(spy).toHaveBeenCalled()
expect(spy.calls.count()).toBe(1)
expect(baseSpy.calls.count()).toBe(1)
expect(mixinSpy.calls.count()).toBe(1)
expect(vm.$el.textContent).toBe('123')
expect(vm.$style).toBe(123)

// Should not be dropped
expect(Test.options.computed.$style()).toBe(123)
expect(Test.options.beforeCreate).toEqual([spy])
expect(Test.options.beforeCreate).toEqual([mixinSpy, baseSpy, spy])
})
})

0 comments on commit 7fa8fa7

Please sign in to comment.