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:
parent
b0d4df9743
commit
ff4d1fcd81
@ -562,6 +562,51 @@ describe('api: options', () => {
|
|||||||
expect(serializeInner(root)).toBe(`<div>1,1,3</div>`)
|
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', () => {
|
describe('warnings', () => {
|
||||||
mockWarn()
|
mockWarn()
|
||||||
|
|
||||||
@ -631,164 +676,6 @@ describe('api: options', () => {
|
|||||||
).toHaveBeenWarned()
|
).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', () => {
|
test('inject property is already declared in props', () => {
|
||||||
const Comp = {
|
const Comp = {
|
||||||
data() {
|
data() {
|
||||||
@ -820,7 +707,159 @@ describe('api: options', () => {
|
|||||||
).toHaveBeenWarned()
|
).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 = {
|
const Comp = {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -852,40 +891,43 @@ describe('api: options', () => {
|
|||||||
const root = nodeOps.createElement('div')
|
const root = nodeOps.createElement('div')
|
||||||
render(h(Comp), root)
|
render(h(Comp), root)
|
||||||
expect(
|
expect(
|
||||||
`Inject property "a" is already defined in Computed.`
|
`Computed property "a" is already defined in Inject.`
|
||||||
).toHaveBeenWarned()
|
).toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('inject property is already declared in methods', () => {
|
test('computed property is already declared in methods', () => {
|
||||||
const Comp = {
|
const Comp = {
|
||||||
data() {
|
computed: {
|
||||||
return {
|
foo() {}
|
||||||
a: 1
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
provide() {
|
|
||||||
return {
|
|
||||||
a: this.a
|
|
||||||
}
|
|
||||||
},
|
|
||||||
render() {
|
|
||||||
return [h(ChildA)]
|
|
||||||
}
|
|
||||||
} as any
|
|
||||||
const ChildA = {
|
|
||||||
methods: {
|
methods: {
|
||||||
a: () => null
|
foo() {}
|
||||||
},
|
},
|
||||||
inject: ['a'],
|
render() {}
|
||||||
render() {
|
}
|
||||||
return this.a
|
|
||||||
}
|
|
||||||
} as any
|
|
||||||
|
|
||||||
const root = nodeOps.createElement('div')
|
const root = nodeOps.createElement('div')
|
||||||
render(h(Comp), root)
|
render(h(Comp), root)
|
||||||
expect(
|
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()
|
).toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -39,7 +39,8 @@ import {
|
|||||||
import {
|
import {
|
||||||
reactive,
|
reactive,
|
||||||
ComputedGetter,
|
ComputedGetter,
|
||||||
WritableComputedOptions
|
WritableComputedOptions,
|
||||||
|
toRaw
|
||||||
} from '@vue/reactivity'
|
} from '@vue/reactivity'
|
||||||
import {
|
import {
|
||||||
ComponentObjectPropsOptions,
|
ComponentObjectPropsOptions,
|
||||||
@ -260,12 +261,15 @@ function createDuplicateChecker() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DataFn = (vm: ComponentPublicInstance) => any
|
||||||
|
|
||||||
export function applyOptions(
|
export function applyOptions(
|
||||||
instance: ComponentInternalInstance,
|
instance: ComponentInternalInstance,
|
||||||
options: ComponentOptions,
|
options: ComponentOptions,
|
||||||
|
deferredData: DataFn[] = [],
|
||||||
|
deferredWatch: ComponentWatchOptions[] = [],
|
||||||
asMixin: boolean = false
|
asMixin: boolean = false
|
||||||
) {
|
) {
|
||||||
const publicThis = instance.proxy!
|
|
||||||
const {
|
const {
|
||||||
// composition
|
// composition
|
||||||
mixins,
|
mixins,
|
||||||
@ -295,6 +299,7 @@ export function applyOptions(
|
|||||||
errorCaptured
|
errorCaptured
|
||||||
} = options
|
} = options
|
||||||
|
|
||||||
|
const publicThis = instance.proxy!
|
||||||
const ctx = instance.ctx
|
const ctx = instance.ctx
|
||||||
const globalMixins = instance.appContext.mixins
|
const globalMixins = instance.appContext.mixins
|
||||||
// call it only during dev
|
// call it only during dev
|
||||||
@ -303,15 +308,15 @@ export function applyOptions(
|
|||||||
if (!asMixin) {
|
if (!asMixin) {
|
||||||
callSyncHook('beforeCreate', options, publicThis, globalMixins)
|
callSyncHook('beforeCreate', options, publicThis, globalMixins)
|
||||||
// global mixins are applied first
|
// global mixins are applied first
|
||||||
applyMixins(instance, globalMixins)
|
applyMixins(instance, globalMixins, deferredData, deferredWatch)
|
||||||
}
|
}
|
||||||
// extending a base component...
|
// extending a base component...
|
||||||
if (extendsOptions) {
|
if (extendsOptions) {
|
||||||
applyOptions(instance, extendsOptions, true)
|
applyOptions(instance, extendsOptions, deferredData, deferredWatch, true)
|
||||||
}
|
}
|
||||||
// local mixins
|
// local mixins
|
||||||
if (mixins) {
|
if (mixins) {
|
||||||
applyMixins(instance, mixins)
|
applyMixins(instance, mixins, deferredData, deferredWatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkDuplicateProperties = __DEV__ ? createDuplicateChecker() : null
|
const checkDuplicateProperties = __DEV__ ? createDuplicateChecker() : null
|
||||||
@ -322,7 +327,55 @@ export function applyOptions(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// state options
|
// options initialization order (to be consistent with Vue 2):
|
||||||
|
// - props (already done outside of this function)
|
||||||
|
// - inject
|
||||||
|
// - methods
|
||||||
|
// - data (deferred since it relies on `this` access)
|
||||||
|
// - computed
|
||||||
|
// - watch (deferred since it relies on `this` access)
|
||||||
|
|
||||||
|
if (injectOptions) {
|
||||||
|
if (isArray(injectOptions)) {
|
||||||
|
for (let i = 0; i < injectOptions.length; i++) {
|
||||||
|
const key = injectOptions[i]
|
||||||
|
ctx[key] = inject(key)
|
||||||
|
if (__DEV__) {
|
||||||
|
checkDuplicateProperties!(OptionTypes.INJECT, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const key in injectOptions) {
|
||||||
|
const opt = injectOptions[key]
|
||||||
|
if (isObject(opt)) {
|
||||||
|
ctx[key] = inject(opt.from, opt.default)
|
||||||
|
} else {
|
||||||
|
ctx[key] = inject(opt)
|
||||||
|
}
|
||||||
|
if (__DEV__) {
|
||||||
|
checkDuplicateProperties!(OptionTypes.INJECT, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (methods) {
|
||||||
|
for (const key in methods) {
|
||||||
|
const methodHandler = (methods as MethodOptions)[key]
|
||||||
|
if (isFunction(methodHandler)) {
|
||||||
|
ctx[key] = methodHandler.bind(publicThis)
|
||||||
|
if (__DEV__) {
|
||||||
|
checkDuplicateProperties!(OptionTypes.METHODS, key)
|
||||||
|
}
|
||||||
|
} else if (__DEV__) {
|
||||||
|
warn(
|
||||||
|
`Method "${key}" has type "${typeof methodHandler}" in the component definition. ` +
|
||||||
|
`Did you reference the function correctly?`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (dataOptions) {
|
if (dataOptions) {
|
||||||
if (__DEV__ && !isFunction(dataOptions)) {
|
if (__DEV__ && !isFunction(dataOptions)) {
|
||||||
warn(
|
warn(
|
||||||
@ -330,33 +383,29 @@ export function applyOptions(
|
|||||||
`Plain object usage is no longer supported.`
|
`Plain object usage is no longer supported.`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const data = dataOptions.call(publicThis, publicThis)
|
|
||||||
if (__DEV__ && isPromise(data)) {
|
if (asMixin) {
|
||||||
warn(
|
deferredData.push(dataOptions as DataFn)
|
||||||
`data() returned a Promise - note data() cannot be async; If you ` +
|
|
||||||
`intend to perform data fetching before component renders, use ` +
|
|
||||||
`async setup() + <Suspense>.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (!isObject(data)) {
|
|
||||||
__DEV__ && warn(`data() should return an object.`)
|
|
||||||
} else if (instance.data === EMPTY_OBJ) {
|
|
||||||
if (__DEV__) {
|
|
||||||
for (const key in data) {
|
|
||||||
checkDuplicateProperties!(OptionTypes.DATA, key)
|
|
||||||
// expose data on ctx during dev
|
|
||||||
Object.defineProperty(ctx, key, {
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
get: () => data[key],
|
|
||||||
set: NOOP
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
instance.data = reactive(data)
|
|
||||||
} else {
|
} else {
|
||||||
// existing data: this is a mixin or extends.
|
resolveData(instance, dataOptions, publicThis)
|
||||||
extend(instance.data, data)
|
}
|
||||||
|
}
|
||||||
|
if (!asMixin) {
|
||||||
|
if (deferredData.length) {
|
||||||
|
deferredData.forEach(dataFn => resolveData(instance, dataFn, publicThis))
|
||||||
|
}
|
||||||
|
if (__DEV__) {
|
||||||
|
const rawData = toRaw(instance.data)
|
||||||
|
for (const key in rawData) {
|
||||||
|
checkDuplicateProperties!(OptionTypes.DATA, key)
|
||||||
|
// expose data on ctx during dev
|
||||||
|
Object.defineProperty(ctx, key, {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
get: () => rawData[key],
|
||||||
|
set: NOOP
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,27 +446,15 @@ export function applyOptions(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (methods) {
|
|
||||||
for (const key in methods) {
|
|
||||||
const methodHandler = (methods as MethodOptions)[key]
|
|
||||||
if (isFunction(methodHandler)) {
|
|
||||||
ctx[key] = methodHandler.bind(publicThis)
|
|
||||||
if (__DEV__) {
|
|
||||||
checkDuplicateProperties!(OptionTypes.METHODS, key)
|
|
||||||
}
|
|
||||||
} else if (__DEV__) {
|
|
||||||
warn(
|
|
||||||
`Method "${key}" has type "${typeof methodHandler}" in the component definition. ` +
|
|
||||||
`Did you reference the function correctly?`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (watchOptions) {
|
if (watchOptions) {
|
||||||
for (const key in watchOptions) {
|
deferredWatch.push(watchOptions)
|
||||||
createWatcher(watchOptions[key], ctx, publicThis, key)
|
}
|
||||||
}
|
if (!asMixin && deferredWatch.length) {
|
||||||
|
deferredWatch.forEach(watchOptions => {
|
||||||
|
for (const key in watchOptions) {
|
||||||
|
createWatcher(watchOptions[key], ctx, publicThis, key)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (provideOptions) {
|
if (provideOptions) {
|
||||||
@ -429,30 +466,6 @@ export function applyOptions(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (injectOptions) {
|
|
||||||
if (isArray(injectOptions)) {
|
|
||||||
for (let i = 0; i < injectOptions.length; i++) {
|
|
||||||
const key = injectOptions[i]
|
|
||||||
ctx[key] = inject(key)
|
|
||||||
if (__DEV__) {
|
|
||||||
checkDuplicateProperties!(OptionTypes.INJECT, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const key in injectOptions) {
|
|
||||||
const opt = injectOptions[key]
|
|
||||||
if (isObject(opt)) {
|
|
||||||
ctx[key] = inject(opt.from, opt.default)
|
|
||||||
} else {
|
|
||||||
ctx[key] = inject(opt)
|
|
||||||
}
|
|
||||||
if (__DEV__) {
|
|
||||||
checkDuplicateProperties!(OptionTypes.INJECT, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// asset options
|
// asset options
|
||||||
if (components) {
|
if (components) {
|
||||||
extend(instance.components, components)
|
extend(instance.components, components)
|
||||||
@ -536,10 +549,35 @@ function callHookFromMixins(
|
|||||||
|
|
||||||
function applyMixins(
|
function applyMixins(
|
||||||
instance: ComponentInternalInstance,
|
instance: ComponentInternalInstance,
|
||||||
mixins: ComponentOptions[]
|
mixins: ComponentOptions[],
|
||||||
|
deferredData: DataFn[],
|
||||||
|
deferredWatch: ComponentWatchOptions[]
|
||||||
) {
|
) {
|
||||||
for (let i = 0; i < mixins.length; i++) {
|
for (let i = 0; i < mixins.length; i++) {
|
||||||
applyOptions(instance, mixins[i], true)
|
applyOptions(instance, mixins[i], deferredData, deferredWatch, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveData(
|
||||||
|
instance: ComponentInternalInstance,
|
||||||
|
dataFn: DataFn,
|
||||||
|
publicThis: ComponentPublicInstance
|
||||||
|
) {
|
||||||
|
const data = dataFn.call(publicThis, publicThis)
|
||||||
|
if (__DEV__ && isPromise(data)) {
|
||||||
|
warn(
|
||||||
|
`data() returned a Promise - note data() cannot be async; If you ` +
|
||||||
|
`intend to perform data fetching before component renders, use ` +
|
||||||
|
`async setup() + <Suspense>.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (!isObject(data)) {
|
||||||
|
__DEV__ && warn(`data() should return an object.`)
|
||||||
|
} else if (instance.data === EMPTY_OBJ) {
|
||||||
|
instance.data = reactive(data)
|
||||||
|
} else {
|
||||||
|
// existing data: this is a mixin or extends.
|
||||||
|
extend(instance.data, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user