avoid duplicate lifecycle hooks during constructor resolution

This commit is contained in:
Evan You 2017-02-23 21:55:47 -05:00
parent 673acecc34
commit 7fa8fa76fe
2 changed files with 41 additions and 14 deletions

View File

@ -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,
@ -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
}
}

View File

@ -87,7 +87,12 @@ 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
@ -95,23 +100,28 @@ describe('Global API: mixin', () => {
$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])
})
})