fix(runtime-core): mixin options that rely on this context should be deferred

Also ensure consistent option apply order with Vue 2, close #1016, close #1029
This commit is contained in:
Evan You
2020-04-22 16:36:18 -04:00
parent b0d4df9743
commit ff4d1fcd81
2 changed files with 340 additions and 260 deletions

View File

@@ -562,6 +562,51 @@ describe('api: options', () => {
expect(serializeInner(root)).toBe(`<div>1,1,3</div>`)
})
// #1016
test('watcher initialization should be deferred in mixins', async () => {
const mixin1 = {
data() {
return {
mixin1Data: 'mixin1'
}
},
methods: {}
}
const watchSpy = jest.fn()
const mixin2 = {
watch: {
mixin3Data: watchSpy
}
}
const mixin3 = {
data() {
return {
mixin3Data: 'mixin3'
}
},
methods: {}
}
let vm: any
const Comp = {
mixins: [mixin1, mixin2, mixin3],
render() {},
created() {
vm = this
}
}
const root = nodeOps.createElement('div')
render(h(Comp), root)
// should have no warnings
vm.mixin3Data = 'hello'
await nextTick()
expect(watchSpy.mock.calls[0].slice(0, 2)).toEqual(['hello', 'mixin3'])
})
describe('warnings', () => {
mockWarn()
@@ -631,164 +676,6 @@ describe('api: options', () => {
).toHaveBeenWarned()
})
test('data property is already declared in props', () => {
const Comp = {
props: { foo: Number },
data: () => ({
foo: 1
}),
render() {}
}
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
`Data property "foo" is already defined in Props.`
).toHaveBeenWarned()
})
test('computed property is already declared in data', () => {
const Comp = {
data: () => ({
foo: 1
}),
computed: {
foo() {}
},
render() {}
}
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
`Computed property "foo" is already defined in Data.`
).toHaveBeenWarned()
})
test('computed property is already declared in props', () => {
const Comp = {
props: { foo: Number },
computed: {
foo() {}
},
render() {}
}
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
`Computed property "foo" is already defined in Props.`
).toHaveBeenWarned()
})
test('methods property is not a function', () => {
const Comp = {
methods: {
foo: 1
},
render() {}
}
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
`Method "foo" has type "number" in the component definition. ` +
`Did you reference the function correctly?`
).toHaveBeenWarned()
})
test('methods property is already declared in data', () => {
const Comp = {
data: () => ({
foo: 2
}),
methods: {
foo() {}
},
render() {}
}
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
`Methods property "foo" is already defined in Data.`
).toHaveBeenWarned()
})
test('methods property is already declared in props', () => {
const Comp = {
props: {
foo: Number
},
methods: {
foo() {}
},
render() {}
}
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
`Methods property "foo" is already defined in Props.`
).toHaveBeenWarned()
})
test('methods property is already declared in computed', () => {
const Comp = {
computed: {
foo: {
get() {},
set() {}
}
},
methods: {
foo() {}
},
render() {}
}
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
`Methods property "foo" is already defined in Computed.`
).toHaveBeenWarned()
})
test('inject property is already declared in data', () => {
const Comp = {
data() {
return {
a: 1
}
},
provide() {
return {
a: this.a
}
},
render() {
return [h(ChildA)]
}
} as any
const ChildA = {
data() {
return {
a: 1
}
},
inject: ['a'],
render() {
return this.a
}
} as any
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
`Inject property "a" is already defined in Data.`
).toHaveBeenWarned()
})
test('inject property is already declared in props', () => {
const Comp = {
data() {
@@ -820,7 +707,159 @@ describe('api: options', () => {
).toHaveBeenWarned()
})
test('inject property is already declared in computed', () => {
test('methods property is not a function', () => {
const Comp = {
methods: {
foo: 1
},
render() {}
}
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
`Method "foo" has type "number" in the component definition. ` +
`Did you reference the function correctly?`
).toHaveBeenWarned()
})
test('methods property is already declared in props', () => {
const Comp = {
props: {
foo: Number
},
methods: {
foo() {}
},
render() {}
}
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
`Methods property "foo" is already defined in Props.`
).toHaveBeenWarned()
})
test('methods property is already declared in inject', () => {
const Comp = {
data() {
return {
a: 1
}
},
provide() {
return {
a: this.a
}
},
render() {
return [h(ChildA)]
}
} as any
const ChildA = {
methods: {
a: () => null
},
inject: ['a'],
render() {
return this.a
}
} as any
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
`Methods property "a" is already defined in Inject.`
).toHaveBeenWarned()
})
test('data property is already declared in props', () => {
const Comp = {
props: { foo: Number },
data: () => ({
foo: 1
}),
render() {}
}
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
`Data property "foo" is already defined in Props.`
).toHaveBeenWarned()
})
test('data property is already declared in inject', () => {
const Comp = {
data() {
return {
a: 1
}
},
provide() {
return {
a: this.a
}
},
render() {
return [h(ChildA)]
}
} as any
const ChildA = {
data() {
return {
a: 1
}
},
inject: ['a'],
render() {
return this.a
}
} as any
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
`Data property "a" is already defined in Inject.`
).toHaveBeenWarned()
})
test('data property is already declared in methods', () => {
const Comp = {
data: () => ({
foo: 1
}),
methods: {
foo() {}
},
render() {}
}
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
`Data property "foo" is already defined in Methods.`
).toHaveBeenWarned()
})
test('computed property is already declared in props', () => {
const Comp = {
props: { foo: Number },
computed: {
foo() {}
},
render() {}
}
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
`Computed property "foo" is already defined in Props.`
).toHaveBeenWarned()
})
test('computed property is already declared in inject', () => {
const Comp = {
data() {
return {
@@ -852,40 +891,43 @@ describe('api: options', () => {
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
`Inject property "a" is already defined in Computed.`
`Computed property "a" is already defined in Inject.`
).toHaveBeenWarned()
})
test('inject property is already declared in methods', () => {
test('computed property is already declared in methods', () => {
const Comp = {
data() {
return {
a: 1
}
computed: {
foo() {}
},
provide() {
return {
a: this.a
}
},
render() {
return [h(ChildA)]
}
} as any
const ChildA = {
methods: {
a: () => null
foo() {}
},
inject: ['a'],
render() {
return this.a
}
} as any
render() {}
}
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
`Inject property "a" is already defined in Methods.`
`Computed property "foo" is already defined in Methods.`
).toHaveBeenWarned()
})
test('computed property is already declared in data', () => {
const Comp = {
data: () => ({
foo: 1
}),
computed: {
foo() {}
},
render() {}
}
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
`Computed property "foo" is already defined in Data.`
).toHaveBeenWarned()
})
})