wip: data option compat

This commit is contained in:
Evan You 2021-04-06 09:31:47 -04:00
parent 53b8127a9c
commit d0da0028f2
5 changed files with 53 additions and 10 deletions

View File

@ -0,0 +1,15 @@
import { isPlainObject } from '@vue/shared'
import { DeprecationTypes, warnDeprecation } from './deprecations'
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, key)
deepMergeData(toVal, fromVal)
} else {
to[key] = fromVal
}
}
}

View File

@ -1,4 +1,5 @@
import { isRuntimeOnly } from '../component' import { isRuntimeOnly } from '../component'
import { warn } from '../warning'
export const enum DeprecationTypes { export const enum DeprecationTypes {
CONFIG_SILENT, CONFIG_SILENT,
@ -16,11 +17,14 @@ export const enum DeprecationTypes {
INSTANCE_SET, INSTANCE_SET,
INSTANCE_DELETE, INSTANCE_DELETE,
INSTANCE_MOUNT, INSTANCE_MOUNT,
INSTANCE_DESTROY INSTANCE_DESTROY,
OPTIONS_DATA_FN,
OPTIONS_DATA_MERGE
} }
type DeprecationData = { type DeprecationData = {
message: string | (() => string) message: string | ((...args: any[]) => string)
link?: string link?: string
} }
@ -120,17 +124,31 @@ const deprecations: Record<DeprecationTypes, DeprecationData> = {
[DeprecationTypes.INSTANCE_DESTROY]: { [DeprecationTypes.INSTANCE_DESTROY]: {
message: `vm.$destroy() has been removed. Use app.unmount() instead.`, message: `vm.$destroy() has been removed. Use app.unmount() instead.`,
link: `https://v3.vuejs.org/api/application-api.html#unmount` link: `https://v3.vuejs.org/api/application-api.html#unmount`
},
[DeprecationTypes.OPTIONS_DATA_FN]: {
message:
`The "data" option can no longer be a plain object. ` +
`Always use a function.`,
link: `https://v3.vuejs.org/guide/migration/data-option.html`
},
[DeprecationTypes.OPTIONS_DATA_MERGE]: {
message: (key: string) =>
`Detected conflicting key "${key}" when merging "data" option values. ` +
`In Vue 3, data keys are merged shallowly and will override one another.`,
link: `https://v3.vuejs.org/guide/migration/data-option.html#mixin-merge-behavior-change`
} }
} }
export function warnDeprecation(key: DeprecationTypes) { export function warnDeprecation(key: DeprecationTypes, ...args: any[]) {
if (!__COMPAT__ || !__DEV__) { if (!__COMPAT__ || !__DEV__) {
return return
} }
const { message, link } = deprecations[key] const { message, link } = deprecations[key]
console.warn( warn(
`[Vue Deprecation]: ${typeof message === 'function' ? message() : message}${ `[DEPRECATION] ${
link ? `\nFor more details, see ${link}` : `` typeof message === 'function' ? message(...args) : message
}` }${link ? `\nFor more details, see ${link}` : ``}`
) )
} }

View File

@ -162,9 +162,14 @@ export function createCompatVue(
if (!inlineOptions) { if (!inlineOptions) {
return createCompatApp(options, SubVue) return createCompatApp(options, SubVue)
} else { } else {
const { el, data } = inlineOptions
if (data && !isFunction(data)) {
__DEV__ && warnDeprecation(DeprecationTypes.OPTIONS_DATA_FN)
inlineOptions.data = () => data
}
return createCompatApp( return createCompatApp(
{ {
el: inlineOptions.el, el,
extends: options, extends: options,
mixins: [inlineOptions] mixins: [inlineOptions]
}, },

View File

@ -65,6 +65,7 @@ import { warn } from './warning'
import { VNodeChild } from './vnode' import { VNodeChild } from './vnode'
import { callWithAsyncErrorHandling } from './errorHandling' import { callWithAsyncErrorHandling } from './errorHandling'
import { UnionToIntersection } from './helpers/typeUtils' import { UnionToIntersection } from './helpers/typeUtils'
import { deepMergeData } from './compat/data'
/** /**
* Interface for declaring custom options. * Interface for declaring custom options.
@ -904,9 +905,13 @@ function resolveData(
instance.data = reactive(data) instance.data = reactive(data)
} else { } else {
// existing data: this is a mixin or extends. // existing data: this is a mixin or extends.
if (__COMPAT__) {
deepMergeData(instance.data, data)
} else {
extend(instance.data, data) extend(instance.data, data)
} }
} }
}
function createWatcher( function createWatcher(
raw: ComponentWatchOptionItem, raw: ComponentWatchOptionItem,

View File

@ -71,7 +71,7 @@ export function warn(msg: string, ...args: any[]) {
resetTracking() resetTracking()
} }
function getComponentTrace(): ComponentTraceStack { export function getComponentTrace(): ComponentTraceStack {
let currentVNode: VNode | null = stack[stack.length - 1] let currentVNode: VNode | null = stack[stack.length - 1]
if (!currentVNode) { if (!currentVNode) {
return [] return []