|
|
@@ -39,7 +39,8 @@ import {
|
|
|
import {
|
|
|
reactive,
|
|
|
ComputedGetter,
|
|
|
- WritableComputedOptions
|
|
|
+ WritableComputedOptions,
|
|
|
+ toRaw
|
|
|
} from '@vue/reactivity'
|
|
|
import {
|
|
|
ComponentObjectPropsOptions,
|
|
|
@@ -260,12 +261,15 @@ function createDuplicateChecker() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+type DataFn = (vm: ComponentPublicInstance) => any
|
|
|
+
|
|
|
export function applyOptions(
|
|
|
instance: ComponentInternalInstance,
|
|
|
options: ComponentOptions,
|
|
|
+ deferredData: DataFn[] = [],
|
|
|
+ deferredWatch: ComponentWatchOptions[] = [],
|
|
|
asMixin: boolean = false
|
|
|
) {
|
|
|
- const publicThis = instance.proxy!
|
|
|
const {
|
|
|
// composition
|
|
|
mixins,
|
|
|
@@ -295,6 +299,7 @@ export function applyOptions(
|
|
|
errorCaptured
|
|
|
} = options
|
|
|
|
|
|
+ const publicThis = instance.proxy!
|
|
|
const ctx = instance.ctx
|
|
|
const globalMixins = instance.appContext.mixins
|
|
|
// call it only during dev
|
|
|
@@ -303,15 +308,15 @@ export function applyOptions(
|
|
|
if (!asMixin) {
|
|
|
callSyncHook('beforeCreate', options, publicThis, globalMixins)
|
|
|
// global mixins are applied first
|
|
|
- applyMixins(instance, globalMixins)
|
|
|
+ applyMixins(instance, globalMixins, deferredData, deferredWatch)
|
|
|
}
|
|
|
// extending a base component...
|
|
|
if (extendsOptions) {
|
|
|
- applyOptions(instance, extendsOptions, true)
|
|
|
+ applyOptions(instance, extendsOptions, deferredData, deferredWatch, true)
|
|
|
}
|
|
|
// local mixins
|
|
|
if (mixins) {
|
|
|
- applyMixins(instance, mixins)
|
|
|
+ applyMixins(instance, mixins, deferredData, deferredWatch)
|
|
|
}
|
|
|
|
|
|
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 (__DEV__ && !isFunction(dataOptions)) {
|
|
|
warn(
|
|
|
@@ -330,33 +383,29 @@ export function applyOptions(
|
|
|
`Plain object usage is no longer supported.`
|
|
|
)
|
|
|
}
|
|
|
- const data = dataOptions.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 (asMixin) {
|
|
|
+ deferredData.push(dataOptions as DataFn)
|
|
|
+ } else {
|
|
|
+ resolveData(instance, dataOptions, publicThis)
|
|
|
}
|
|
|
- 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
|
|
|
- })
|
|
|
- }
|
|
|
+ }
|
|
|
+ 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
|
|
|
+ })
|
|
|
}
|
|
|
- instance.data = reactive(data)
|
|
|
- } else {
|
|
|
- // existing data: this is a mixin or extends.
|
|
|
- extend(instance.data, data)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -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) {
|
|
|
- for (const key in watchOptions) {
|
|
|
- createWatcher(watchOptions[key], ctx, publicThis, key)
|
|
|
- }
|
|
|
+ deferredWatch.push(watchOptions)
|
|
|
+ }
|
|
|
+ if (!asMixin && deferredWatch.length) {
|
|
|
+ deferredWatch.forEach(watchOptions => {
|
|
|
+ for (const key in watchOptions) {
|
|
|
+ createWatcher(watchOptions[key], ctx, publicThis, key)
|
|
|
+ }
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
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
|
|
|
if (components) {
|
|
|
extend(instance.components, components)
|
|
|
@@ -536,10 +549,35 @@ function callHookFromMixins(
|
|
|
|
|
|
function applyMixins(
|
|
|
instance: ComponentInternalInstance,
|
|
|
- mixins: ComponentOptions[]
|
|
|
+ mixins: ComponentOptions[],
|
|
|
+ deferredData: DataFn[],
|
|
|
+ deferredWatch: ComponentWatchOptions[]
|
|
|
) {
|
|
|
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)
|
|
|
}
|
|
|
}
|
|
|
|