fix(runtime-core): fix data merge order for mixins/extends

fix #1953
This commit is contained in:
Evan You 2020-08-24 21:30:04 -04:00
parent b742384313
commit c15311cfe8
2 changed files with 30 additions and 28 deletions

View File

@ -446,7 +446,7 @@ describe('api: options', () => {
calls.push('mixinA created') calls.push('mixinA created')
expect(this.a).toBe(1) expect(this.a).toBe(1)
expect(this.b).toBe(2) expect(this.b).toBe(2)
expect(this.c).toBe(3) expect(this.c).toBe(4)
}, },
mounted() { mounted() {
calls.push('mixinA mounted') calls.push('mixinA mounted')
@ -468,7 +468,7 @@ describe('api: options', () => {
expect(this.a).toBe(1) expect(this.a).toBe(1)
expect(this.b).toBe(2) expect(this.b).toBe(2)
expect(this.bP).toBeUndefined() expect(this.bP).toBeUndefined()
expect(this.c).toBe(3) expect(this.c).toBe(4)
expect(this.cP1).toBeUndefined() expect(this.cP1).toBeUndefined()
}, },
mounted() { mounted() {
@ -484,7 +484,8 @@ describe('api: options', () => {
}, },
created() { created() {
calls.push('mixinC created') calls.push('mixinC created')
expect(this.c).toBe(3) // component data() should overwrite mixin field with same key
expect(this.c).toBe(4)
expect(this.cP1).toBeUndefined() expect(this.cP1).toBeUndefined()
}, },
mounted() { mounted() {
@ -498,6 +499,7 @@ describe('api: options', () => {
mixins: [defineComponent(mixinA), defineComponent(mixinB), mixinC], mixins: [defineComponent(mixinA), defineComponent(mixinB), mixinC],
data() { data() {
return { return {
c: 4,
z: 4 z: 4
} }
}, },
@ -506,7 +508,7 @@ describe('api: options', () => {
expect(this.a).toBe(1) expect(this.a).toBe(1)
expect(this.b).toBe(2) expect(this.b).toBe(2)
expect(this.bP).toBeUndefined() expect(this.bP).toBeUndefined()
expect(this.c).toBe(3) expect(this.c).toBe(4)
expect(this.cP2).toBeUndefined() expect(this.cP2).toBeUndefined()
expect(this.z).toBe(4) expect(this.z).toBe(4)
}, },
@ -517,7 +519,7 @@ describe('api: options', () => {
return `${this.a}${this.b}${this.c}` return `${this.a}${this.b}${this.c}`
} }
}) })
expect(renderToString(h(Comp))).toBe(`123`) expect(renderToString(h(Comp))).toBe(`124`)
expect(calls).toEqual([ expect(calls).toEqual([
'mixinA created', 'mixinA created',
'mixinB created', 'mixinB created',
@ -546,7 +548,8 @@ describe('api: options', () => {
const Base = { const Base = {
data() { data() {
return { return {
a: 1 a: 1,
b: 1
} }
}, },
methods: { methods: {
@ -582,7 +585,8 @@ describe('api: options', () => {
const Base = { const Base = {
data() { data() {
return { return {
a: 1 a: 1,
x: 'base'
} }
}, },
methods: { methods: {
@ -595,22 +599,23 @@ describe('api: options', () => {
calls.push('base') calls.push('base')
} }
} }
const Base2 = { const Mixin = {
data() { data() {
return { return {
b: true b: true,
x: 'mixin'
} }
}, },
mounted(this: any) { mounted(this: any) {
expect(this.a).toBe(1) expect(this.a).toBe(1)
expect(this.b).toBeTruthy() expect(this.b).toBeTruthy()
expect(this.c).toBe(2) expect(this.c).toBe(2)
calls.push('base2') calls.push('mixin')
} }
} }
const Comp = defineComponent({ const Comp = defineComponent({
extends: defineComponent(Base), extends: defineComponent(Base),
mixins: [defineComponent(Base2)], mixins: [defineComponent(Mixin)],
data() { data() {
return { return {
c: 2 c: 2
@ -620,12 +625,12 @@ describe('api: options', () => {
calls.push('comp') calls.push('comp')
}, },
render() { render() {
return `${this.a}${this.b}${this.c}` return `${this.a}${this.b}${this.c}${this.x}`
} }
}) })
expect(renderToString(h(Comp))).toBe(`1true2`) expect(renderToString(h(Comp))).toBe(`1true2mixin`)
expect(calls).toEqual(['base', 'base2', 'comp']) expect(calls).toEqual(['base', 'mixin', 'comp'])
}) })
test('accessing setup() state from options', async () => { test('accessing setup() state from options', async () => {

View File

@ -485,24 +485,13 @@ export function applyOptions(
} }
} }
if (dataOptions) {
if (__DEV__ && !isFunction(dataOptions)) {
warn(
`The data option must be a function. ` +
`Plain object usage is no longer supported.`
)
}
if (asMixin) {
deferredData.push(dataOptions as DataFn)
} else {
resolveData(instance, dataOptions, publicThis)
}
}
if (!asMixin) { if (!asMixin) {
if (deferredData.length) { if (deferredData.length) {
deferredData.forEach(dataFn => resolveData(instance, dataFn, publicThis)) deferredData.forEach(dataFn => resolveData(instance, dataFn, publicThis))
} }
if (dataOptions) {
resolveData(instance, dataOptions, publicThis)
}
if (__DEV__) { if (__DEV__) {
const rawData = toRaw(instance.data) const rawData = toRaw(instance.data)
for (const key in rawData) { for (const key in rawData) {
@ -518,6 +507,8 @@ export function applyOptions(
} }
} }
} }
} else if (dataOptions) {
deferredData.push(dataOptions as DataFn)
} }
if (computedOptions) { if (computedOptions) {
@ -666,6 +657,12 @@ function resolveData(
dataFn: DataFn, dataFn: DataFn,
publicThis: ComponentPublicInstance publicThis: ComponentPublicInstance
) { ) {
if (__DEV__ && !isFunction(dataFn)) {
warn(
`The data option must be a function. ` +
`Plain object usage is no longer supported.`
)
}
const data = dataFn.call(publicThis, publicThis) const data = dataFn.call(publicThis, publicThis)
if (__DEV__ && isPromise(data)) { if (__DEV__ && isPromise(data)) {
warn( warn(