Browse Source

wip: data option compat

Evan You 5 years ago
parent
commit
d0da0028f2

+ 15 - 0
packages/runtime-core/src/compat/data.ts

@@ -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
+    }
+  }
+}

+ 25 - 7
packages/runtime-core/src/compat/deprecations.ts

@@ -1,4 +1,5 @@
 import { isRuntimeOnly } from '../component'
+import { warn } from '../warning'
 
 export const enum DeprecationTypes {
   CONFIG_SILENT,
@@ -16,11 +17,14 @@ export const enum DeprecationTypes {
   INSTANCE_SET,
   INSTANCE_DELETE,
   INSTANCE_MOUNT,
-  INSTANCE_DESTROY
+  INSTANCE_DESTROY,
+
+  OPTIONS_DATA_FN,
+  OPTIONS_DATA_MERGE
 }
 
 type DeprecationData = {
-  message: string | (() => string)
+  message: string | ((...args: any[]) => string)
   link?: string
 }
 
@@ -120,17 +124,31 @@ const deprecations: Record<DeprecationTypes, DeprecationData> = {
   [DeprecationTypes.INSTANCE_DESTROY]: {
     message: `vm.$destroy() has been removed. Use app.unmount() instead.`,
     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__) {
     return
   }
   const { message, link } = deprecations[key]
-  console.warn(
-    `[Vue Deprecation]: ${typeof message === 'function' ? message() : message}${
-      link ? `\nFor more details, see ${link}` : ``
-    }`
+  warn(
+    `[DEPRECATION] ${
+      typeof message === 'function' ? message(...args) : message
+    }${link ? `\nFor more details, see ${link}` : ``}`
   )
 }

+ 6 - 1
packages/runtime-core/src/compat/global.ts

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

+ 6 - 1
packages/runtime-core/src/componentOptions.ts

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

+ 1 - 1
packages/runtime-core/src/warning.ts

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