]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: async component compat
authorEvan You <yyx990803@gmail.com>
Thu, 8 Apr 2021 20:11:28 +0000 (16:11 -0400)
committerEvan You <yyx990803@gmail.com>
Thu, 8 Apr 2021 20:11:28 +0000 (16:11 -0400)
packages/runtime-core/src/compat/component.ts [new file with mode: 0644]
packages/runtime-core/src/compat/deprecations.ts
packages/runtime-core/src/vnode.ts

diff --git a/packages/runtime-core/src/compat/component.ts b/packages/runtime-core/src/compat/component.ts
new file mode 100644 (file)
index 0000000..ca15a65
--- /dev/null
@@ -0,0 +1,83 @@
+import { isArray, isFunction, isObject, isPromise } from '@vue/shared/src'
+import { defineAsyncComponent } from '../apiAsyncComponent'
+import { Component, ComponentOptions, FunctionalComponent } from '../component'
+import { isVNode } from '../vnode'
+import { softAssertCompatEnabled } from './compatConfig'
+import { DeprecationTypes } from './deprecations'
+
+export function convertLegacyComponent(comp: any): Component {
+  // 2.x async component
+  if (
+    isFunction(comp) &&
+    softAssertCompatEnabled(DeprecationTypes.COMPONENT_ASYNC, comp)
+  ) {
+    return convertLegacyAsyncComponent(comp)
+  }
+
+  // 2.x functional component
+  if (
+    isObject(comp) &&
+    comp.functional &&
+    softAssertCompatEnabled(DeprecationTypes.COMPONENT_FUNCTIONAL, comp)
+  ) {
+    return convertLegacyFunctionalComponent(comp)
+  }
+
+  return comp
+}
+
+interface LegacyAsyncOptions {
+  component: Promise<Component>
+  loading?: Component
+  error?: Component
+  delay?: number
+  timeout?: number
+}
+
+type LegacyAsyncReturnValue = Promise<Component> | LegacyAsyncOptions
+
+type LegacyAsyncComponent = (
+  resolve?: (res: LegacyAsyncReturnValue) => void,
+  reject?: (reason?: any) => void
+) => LegacyAsyncReturnValue | undefined
+
+const normalizedAsyncComponentMap = new Map<LegacyAsyncComponent, Component>()
+
+function convertLegacyAsyncComponent(comp: LegacyAsyncComponent) {
+  if (normalizedAsyncComponentMap.has(comp)) {
+    return normalizedAsyncComponentMap.get(comp)!
+  }
+
+  // we have to call the function here due to how v2's API won't expose the
+  // options until we call it
+  let resolve: (res: LegacyAsyncReturnValue) => void
+  let reject: (reason?: any) => void
+  const fallbackPromise = new Promise<Component>((r, rj) => {
+    ;(resolve = r), (reject = rj)
+  })
+
+  const res = comp(resolve!, reject!)
+
+  let converted: Component
+  if (isPromise(res)) {
+    converted = defineAsyncComponent(() => res)
+  } else if (isObject(res) && !isVNode(res) && !isArray(res)) {
+    converted = defineAsyncComponent({
+      loader: () => res.component,
+      loadingComponent: res.loading,
+      errorComponent: res.error,
+      delay: res.delay,
+      timeout: res.timeout
+    })
+  } else if (res == null) {
+    converted = defineAsyncComponent(() => fallbackPromise)
+  } else {
+    converted = comp as any // probably a v3 functional comp
+  }
+  normalizedAsyncComponentMap.set(comp, converted)
+  return converted
+}
+
+function convertLegacyFunctionalComponent(comp: ComponentOptions) {
+  return comp.render as FunctionalComponent
+}
index b34cd716082f1e782dd47b440e189c6edb4bf42a..5f6e7b28f0bbeee494f33b30bc35d2215c30b211 100644 (file)
@@ -1,5 +1,6 @@
 import {
   formatComponentName,
+  getComponentName,
   getCurrentInstance,
   isRuntimeOnly
 } from '../component'
@@ -46,7 +47,10 @@ export const enum DeprecationTypes {
   ATTR_ENUMERATED_COERSION = 'ATTR_ENUMERATED_COERSION',
 
   TRANSITION_CLASSES = 'TRANSITION_CLASSES',
-  TRANSITION_GROUP_ROOT = 'TRANSITION_GROUP_ROOT'
+  TRANSITION_GROUP_ROOT = 'TRANSITION_GROUP_ROOT',
+
+  COMPONENT_ASYNC = 'COMPONENT_ASYNC',
+  COMPONENT_FUNCTIONAL = 'COMPONENT_FUNCTIONAL'
 }
 
 type DeprecationData = {
@@ -302,6 +306,39 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = {
         DeprecationTypes.TRANSITION_GROUP_ROOT
       }: { enabled: false }})\n`,
     link: `https://v3.vuejs.org/guide/migration/transition-group.html`
+  },
+
+  [DeprecationTypes.COMPONENT_ASYNC]: {
+    message: (comp: any) => {
+      const name = getComponentName(comp)
+      return (
+        `Async component${
+          name ? ` <${name}>` : `s`
+        } should be explicitly created via \`defineAsyncComponent()\` ` +
+        `in Vue 3. Plain functions will be treated as functional components in ` +
+        `non-compat build.`
+      )
+    },
+    link: `https://v3.vuejs.org/guide/migration/async-components.html`
+  },
+
+  [DeprecationTypes.COMPONENT_FUNCTIONAL]: {
+    message: (comp: any) => {
+      const name = getComponentName(comp)
+      return (
+        `Functional component${
+          name ? ` <${name}>` : `s`
+        } should be defined as a plain function in Vue 3. The "functional" ` +
+        `option has been removed.\n` +
+        `NOTE: Before migrating, ensure that all async ` +
+        `components have been upgraded to use \`defineAsyncComponent()\` and ` +
+        `then disable compat for legacy async components with:` +
+        `\n\n  configureCompat({ ${
+          DeprecationTypes.COMPONENT_ASYNC
+        }: { enabled: false }})\n`
+      )
+    },
+    link: `https://v3.vuejs.org/guide/migration/functional-components.html`
   }
 }
 
index e46c9842619059c98c0dc96d9142cc390b022ea9..364214de935381bc1541fac4d57c3abc17e07f45 100644 (file)
@@ -41,6 +41,7 @@ import { RendererNode, RendererElement } from './renderer'
 import { NULL_DYNAMIC_COMPONENT } from './helpers/resolveAssets'
 import { hmrDirtyComponents } from './hmr'
 import { setCompiledSlotRendering } from './helpers/renderSlot'
+import { convertLegacyComponent } from './compat/component'
 
 export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as {
   __isFragment: true
@@ -358,6 +359,11 @@ function _createVNode(
     type = type.__vccOpts
   }
 
+  // 2.x async/functional component compat
+  if (__COMPAT__) {
+    type = convertLegacyComponent(type)
+  }
+
   // class & style normalization.
   if (props) {
     // for reactive or proxy objects, we need to clone it to enable mutation.