fix(runtime-core): align option merge behavior with Vue 2

fix #3566, #2791
This commit is contained in:
Evan You
2021-06-02 14:37:27 -04:00
parent 1e35a860b9
commit e2ca67b59a
9 changed files with 439 additions and 375 deletions

View File

@@ -531,7 +531,10 @@ const seenConfigObjects = /*#__PURE__*/ new WeakSet<CompatConfig>()
const warnedInvalidKeys: Record<string, boolean> = {}
// dev only
export function validateCompatConfig(config: CompatConfig) {
export function validateCompatConfig(
config: CompatConfig,
instance?: ComponentInternalInstance
) {
if (seenConfigObjects.has(config)) {
return
}
@@ -558,6 +561,14 @@ export function validateCompatConfig(config: CompatConfig) {
warnedInvalidKeys[key] = true
}
}
if (instance && config[DeprecationTypes.OPTIONS_DATA_MERGE] != null) {
warn(
`Deprecation config "${
DeprecationTypes.OPTIONS_DATA_MERGE
}" can only be configured globally.`
)
}
}
export function getCompatConfigForKey(

View File

@@ -1,39 +1,16 @@
import { isFunction, isPlainObject } from '@vue/shared'
import { ComponentInternalInstance } from '../component'
import { ComponentPublicInstance } from '../componentPublicInstance'
import { isPlainObject } from '@vue/shared'
import { DeprecationTypes, warnDeprecation } from './compatConfig'
export function deepMergeData(
to: any,
from: any,
instance: ComponentInternalInstance
) {
export function deepMergeData(to: any, from: any) {
for (const key in from) {
const toVal = to[key]
const fromVal = from[key]
if (key in to && isPlainObject(toVal) && isPlainObject(fromVal)) {
__DEV__ &&
warnDeprecation(DeprecationTypes.OPTIONS_DATA_MERGE, instance, key)
deepMergeData(toVal, fromVal, instance)
__DEV__ && warnDeprecation(DeprecationTypes.OPTIONS_DATA_MERGE, null, key)
deepMergeData(toVal, fromVal)
} else {
to[key] = fromVal
}
}
return to
}
export function mergeDataOption(to: any, from: any) {
if (!from) {
return to
}
if (!to) {
return from
}
return function mergedDataFn(this: ComponentPublicInstance) {
return deepMergeData(
isFunction(to) ? to.call(this, this) : to,
isFunction(from) ? from.call(this, this) : from,
this.$
)
}
}

View File

@@ -34,7 +34,11 @@ import {
isRuntimeOnly,
setupComponent
} from '../component'
import { RenderFunction, mergeOptions } from '../componentOptions'
import {
RenderFunction,
mergeOptions,
internalOptionMergeStrats
} from '../componentOptions'
import { ComponentPublicInstance } from '../componentPublicInstance'
import { devtoolsInitApp, devtoolsUnmountApp } from '../devtools'
import { Directive } from '../directives'
@@ -43,8 +47,7 @@ import { version } from '..'
import {
installLegacyConfigWarnings,
installLegacyOptionMergeStrats,
LegacyConfig,
legacyOptionMergeStrats
LegacyConfig
} from './globalConfig'
import { LegacyDirective } from './customDirective'
import {
@@ -231,8 +234,7 @@ export function createCompatVue(
mergeOptions(
extend({}, SubVue.options),
inlineOptions,
null,
legacyOptionMergeStrats as any
internalOptionMergeStrats as any
),
SubVue
)
@@ -257,8 +259,7 @@ export function createCompatVue(
SubVue.options = mergeOptions(
mergeBase,
extendOptions,
null,
legacyOptionMergeStrats as any
internalOptionMergeStrats as any
)
SubVue.options._base = SubVue
@@ -305,8 +306,7 @@ export function createCompatVue(
mergeOptions(
parent,
child,
vm && vm.$,
vm ? undefined : (legacyOptionMergeStrats as any)
vm ? undefined : (internalOptionMergeStrats as any)
),
defineReactive
}

View File

@@ -1,12 +1,11 @@
import { extend, isArray } from '@vue/shared'
import { AppConfig } from '../apiCreateApp'
import { mergeDataOption } from './data'
import {
DeprecationTypes,
softAssertCompatEnabled,
warnDeprecation
} from './compatConfig'
import { isCopyingConfig } from './global'
import { internalOptionMergeStrats } from '../componentOptions'
// legacy config warnings
export type LegacyConfig = {
@@ -70,60 +69,16 @@ export function installLegacyOptionMergeStrats(config: AppConfig) {
return target[key]
}
if (
key in legacyOptionMergeStrats &&
key in internalOptionMergeStrats &&
softAssertCompatEnabled(
DeprecationTypes.CONFIG_OPTION_MERGE_STRATS,
null
)
) {
return legacyOptionMergeStrats[
key as keyof typeof legacyOptionMergeStrats
return internalOptionMergeStrats[
key as keyof typeof internalOptionMergeStrats
]
}
}
})
}
export const legacyOptionMergeStrats = {
data: mergeDataOption,
beforeCreate: mergeHook,
created: mergeHook,
beforeMount: mergeHook,
mounted: mergeHook,
beforeUpdate: mergeHook,
updated: mergeHook,
beforeDestroy: mergeHook,
destroyed: mergeHook,
activated: mergeHook,
deactivated: mergeHook,
errorCaptured: mergeHook,
serverPrefetch: mergeHook,
// assets
components: mergeObjectOptions,
directives: mergeObjectOptions,
filters: mergeObjectOptions,
// objects
props: mergeObjectOptions,
methods: mergeObjectOptions,
inject: mergeObjectOptions,
computed: mergeObjectOptions,
// watch has special merge behavior in v2, but isn't actually needed in v3.
// since we are only exposing these for compat and nobody should be relying
// on the watch-specific behavior, just expose the object merge strat.
watch: mergeObjectOptions
}
function toArray(target: any) {
return isArray(target) ? target : target ? [target] : []
}
function mergeHook(
to: Function[] | Function | undefined,
from: Function | Function[]
) {
return Array.from(new Set([...toArray(to), ...toArray(from)]))
}
function mergeObjectOptions(to: Object | undefined, from: Object | undefined) {
return to ? extend(extend(Object.create(null), to), from) : from
}