_createRoot?(options: ComponentOptions): ComponentPublicInstance
}
-export type OptionMergeFunction = (
- to: unknown,
- from: unknown,
- instance: any,
- key: string
-) => any
+export type OptionMergeFunction = (to: unknown, from: unknown) => any
export interface AppConfig {
// @private
components: Record<string, Component>
directives: Record<string, Directive>
provides: Record<string | symbol, any>
+
+ /**
+ * Cache for merged/normalized component options
+ * Each app instance has its own cache because app-level global mixins and
+ * optionMergeStrategies can affect merge behavior.
+ */
+ cache: WeakMap<ComponentOptions, ComponentOptions>
/**
* Flag for de-optimizing props normalization
* @internal
mixins: [],
components: {},
directives: {},
- provides: Object.create(null)
+ provides: Object.create(null),
+ cache: new WeakMap()
}
}
DIRECTIVES,
FILTERS
} from './helpers/resolveAssets'
+import { OptionMergeFunction } from './apiCreateApp'
/**
* Interface for declaring custom options.
* @internal
*/
__asyncResolved?: ConcreteComponent
- /**
- * cache for merged $options
- * @internal
- */
- __merged?: ComponentOptions
// Type differentiators ------------------------------------------------------
__differentiator?: keyof D | keyof C | keyof M
}
+type MergedHook<T = (() => void)> = T | T[]
+
+export type MergedComponentOptionsOverride = {
+ beforeCreate?: MergedHook
+ created?: MergedHook
+ beforeMount?: MergedHook
+ mounted?: MergedHook
+ beforeUpdate?: MergedHook
+ updated?: MergedHook
+ activated?: MergedHook
+ deactivated?: MergedHook
+ /** @deprecated use `beforeUnmount` instead */
+ beforeDestroy?: MergedHook
+ beforeUnmount?: MergedHook
+ /** @deprecated use `unmounted` instead */
+ destroyed?: MergedHook
+ unmounted?: MergedHook
+ renderTracked?: MergedHook<DebuggerHook>
+ renderTriggered?: MergedHook<DebuggerHook>
+ errorCaptured?: MergedHook<ErrorCapturedHook>
+}
+
export type OptionTypesKeys = 'P' | 'B' | 'D' | 'C' | 'M' | 'Defaults'
export type OptionTypesType<
}
}
+/**
+ * Resolve merged options and cache it on the component.
+ * This is done only once per-component since the merging does not involve
+ * instances.
+ */
export function resolveMergedOptions(
instance: ComponentInternalInstance
-): ComponentOptions {
- const raw = instance.type as ComponentOptions
- const { __merged, mixins, extends: extendsOptions } = raw
- if (__merged) return __merged
- const globalMixins = instance.appContext.mixins
- if (!globalMixins.length && !mixins && !extendsOptions) return raw
- const options = {}
- globalMixins.forEach(m => mergeOptions(options, m, instance))
- mergeOptions(options, raw, instance)
- return (raw.__merged = options)
+): ComponentOptions & MergedComponentOptionsOverride {
+ const base = instance.type as ComponentOptions
+ const { mixins, extends: extendsOptions } = base
+ const {
+ mixins: globalMixins,
+ cache,
+ config: { optionMergeStrategies }
+ } = instance.appContext
+ const cached = cache.get(base)
+
+ let resolved: ComponentOptions
+
+ if (cached) {
+ resolved = cached
+ } else if (!globalMixins.length && !mixins && !extendsOptions) {
+ if (
+ __COMPAT__ &&
+ isCompatEnabled(DeprecationTypes.PRIVATE_APIS, instance)
+ ) {
+ resolved = extend({}, base)
+ resolved.parent = instance.parent && instance.parent.proxy
+ resolved.propsData = instance.vnode.props
+ } else {
+ resolved = base
+ }
+ } else {
+ resolved = {}
+ if (globalMixins.length) {
+ globalMixins.forEach(m =>
+ mergeOptions(resolved, m, optionMergeStrategies)
+ )
+ }
+ mergeOptions(resolved, base, optionMergeStrategies)
+ }
+
+ cache.set(base, resolved)
+ return resolved
}
export function mergeOptions(
to: any,
from: any,
- instance?: ComponentInternalInstance | null,
- strats = instance && instance.appContext.config.optionMergeStrategies
+ strats: Record<string, OptionMergeFunction>
) {
if (__COMPAT__ && isFunction(from)) {
from = from.options
const { mixins, extends: extendsOptions } = from
- extendsOptions && mergeOptions(to, extendsOptions, instance, strats)
- mixins &&
- mixins.forEach((m: ComponentOptionsMixin) =>
- mergeOptions(to, m, instance, strats)
- )
+ if (extendsOptions) {
+ mergeOptions(to, extendsOptions, strats)
+ }
+ if (mixins) {
+ mixins.forEach((m: ComponentOptionsMixin) => mergeOptions(to, m, strats))
+ }
for (const key in from) {
if (strats && hasOwn(strats, key)) {
- to[key] = strats[key](to[key], from[key], instance && instance.proxy, key)
+ to[key] = strats[key](to[key], from[key])
} else {
to[key] = from[key]
}