]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler-core): support Suspense in templates
authorEvan You <yyx990803@gmail.com>
Wed, 16 Oct 2019 21:40:00 +0000 (17:40 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 16 Oct 2019 21:43:41 +0000 (17:43 -0400)
packages/compiler-core/src/ast.ts
packages/compiler-core/src/parse.ts
packages/compiler-core/src/transforms/transformElement.ts

index 0ca3e2c93d4ec4b0b189de6a8d0f7799940971dd..b2e127c0e27aeea8937a1966fddf99f86634b3a7 100644 (file)
@@ -50,7 +50,8 @@ export const enum ElementTypes {
   COMPONENT,
   SLOT,
   TEMPLATE,
-  PORTAL
+  PORTAL,
+  SUSPENSE
 }
 
 export interface Node {
@@ -101,6 +102,7 @@ export type ElementNode =
   | SlotOutletNode
   | TemplateNode
   | PortalNode
+  | SuspenseNode
 
 export interface BaseElementNode extends Node {
   type: NodeTypes.ELEMENT
@@ -141,6 +143,11 @@ export interface PortalNode extends BaseElementNode {
   codegenNode: ElementCodegenNode | undefined
 }
 
+export interface SuspenseNode extends BaseElementNode {
+  tagType: ElementTypes.SUSPENSE
+  codegenNode: ElementCodegenNode | undefined
+}
+
 export interface TextNode extends Node {
   type: NodeTypes.TEXT
   content: string
index 640298189679c4a6985113c6ff8432d6a4ae642c..138fd2c5ec7b544c9072297e4ab077850672efdb 100644 (file)
@@ -445,6 +445,8 @@ function parseTag(
     if (tag === 'slot') tagType = ElementTypes.SLOT
     else if (tag === 'template') tagType = ElementTypes.TEMPLATE
     else if (tag === 'portal' || tag === 'Portal') tagType = ElementTypes.PORTAL
+    else if (tag === 'suspense' || tag === 'Suspense')
+      tagType = ElementTypes.SUSPENSE
   }
 
   return {
index 183a1f987849d555382186ed6e98eb265e83786d..74ef7f55b5bbea69d9154d15fa2d70bc1ab44131 100644 (file)
@@ -24,7 +24,8 @@ import {
   RESOLVE_COMPONENT,
   MERGE_PROPS,
   TO_HANDLERS,
-  PORTAL
+  PORTAL,
+  SUSPENSE
 } from '../runtimeHelpers'
 import { getInnerRange, isVSlot, toValidAssetId } from '../utils'
 import { buildSlots } from './vSlot'
@@ -36,130 +37,126 @@ const directiveImportMap = new WeakMap<DirectiveNode, symbol>()
 
 // generate a JavaScript AST for this element's codegen
 export const transformElement: NodeTransform = (node, context) => {
-  if (node.type === NodeTypes.ELEMENT) {
-    if (
-      node.tagType === ElementTypes.ELEMENT ||
-      node.tagType === ElementTypes.COMPONENT ||
-      node.tagType === ElementTypes.PORTAL ||
-      // <template> with v-if or v-for are ignored during traversal.
-      // <template> without v-slot should be treated as a normal element.
-      (node.tagType === ElementTypes.TEMPLATE && !node.props.some(isVSlot))
-    ) {
-      // perform the work on exit, after all child expressions have been
-      // processed and merged.
-      return () => {
-        const isComponent = node.tagType === ElementTypes.COMPONENT
-        const isPortal = node.tagType === ElementTypes.PORTAL
-        let hasProps = node.props.length > 0
-        let patchFlag: number = 0
-        let runtimeDirectives: DirectiveNode[] | undefined
-        let dynamicPropNames: string[] | undefined
+  if (
+    node.type !== NodeTypes.ELEMENT ||
+    // handled by transformSlotOutlet
+    node.tagType === ElementTypes.SLOT ||
+    // <template v-if/v-for> should have already been replaced
+    // <templte v-slot> is handled by buildSlots
+    (node.tagType === ElementTypes.TEMPLATE && node.props.some(isVSlot))
+  ) {
+    return
+  }
+  // perform the work on exit, after all child expressions have been
+  // processed and merged.
+  return () => {
+    const isComponent = node.tagType === ElementTypes.COMPONENT
+    let hasProps = node.props.length > 0
+    let patchFlag: number = 0
+    let runtimeDirectives: DirectiveNode[] | undefined
+    let dynamicPropNames: string[] | undefined
 
-        if (isComponent) {
-          context.helper(RESOLVE_COMPONENT)
-          context.components.add(node.tag)
-        }
+    if (isComponent) {
+      context.helper(RESOLVE_COMPONENT)
+      context.components.add(node.tag)
+    }
 
-        const args: CallExpression['arguments'] = [
-          isComponent
-            ? toValidAssetId(node.tag, `component`)
-            : isPortal
-              ? context.helper(PORTAL)
-              : `"${node.tag}"`
-        ]
-        // props
-        if (hasProps) {
-          const propsBuildResult = buildProps(node, context)
-          patchFlag = propsBuildResult.patchFlag
-          dynamicPropNames = propsBuildResult.dynamicPropNames
-          runtimeDirectives = propsBuildResult.directives
-          if (!propsBuildResult.props) {
-            hasProps = false
-          } else {
-            args.push(propsBuildResult.props)
-          }
+    const args: CallExpression['arguments'] = [
+      isComponent
+        ? toValidAssetId(node.tag, `component`)
+        : node.tagType === ElementTypes.PORTAL
+          ? context.helper(PORTAL)
+          : node.tagType === ElementTypes.SUSPENSE
+            ? context.helper(SUSPENSE)
+            : `"${node.tag}"`
+    ]
+    // props
+    if (hasProps) {
+      const propsBuildResult = buildProps(node, context)
+      patchFlag = propsBuildResult.patchFlag
+      dynamicPropNames = propsBuildResult.dynamicPropNames
+      runtimeDirectives = propsBuildResult.directives
+      if (!propsBuildResult.props) {
+        hasProps = false
+      } else {
+        args.push(propsBuildResult.props)
+      }
+    }
+    // children
+    const hasChildren = node.children.length > 0
+    if (hasChildren) {
+      if (!hasProps) {
+        args.push(`null`)
+      }
+      if (isComponent) {
+        const { slots, hasDynamicSlots } = buildSlots(node, context)
+        args.push(slots)
+        if (hasDynamicSlots) {
+          patchFlag |= PatchFlags.DYNAMIC_SLOTS
         }
-        // children
-        const hasChildren = node.children.length > 0
-        if (hasChildren) {
-          if (!hasProps) {
-            args.push(`null`)
-          }
-          if (isComponent) {
-            const { slots, hasDynamicSlots } = buildSlots(node, context)
-            args.push(slots)
-            if (hasDynamicSlots) {
-              patchFlag |= PatchFlags.DYNAMIC_SLOTS
-            }
-          } else if (node.children.length === 1) {
-            const child = node.children[0]
-            const type = child.type
-            // check for dynamic text children
-            const hasDynamicTextChild =
-              type === NodeTypes.INTERPOLATION ||
-              type === NodeTypes.COMPOUND_EXPRESSION
-            if (hasDynamicTextChild && !isStaticNode(child)) {
-              patchFlag |= PatchFlags.TEXT
-            }
-            // pass directly if the only child is a text node
-            // (plain / interpolation / expression)
-            if (hasDynamicTextChild || type === NodeTypes.TEXT) {
-              args.push(child)
-            } else {
-              args.push(node.children)
-            }
-          } else {
-            args.push(node.children)
-          }
+      } else if (node.children.length === 1) {
+        const child = node.children[0]
+        const type = child.type
+        // check for dynamic text children
+        const hasDynamicTextChild =
+          type === NodeTypes.INTERPOLATION ||
+          type === NodeTypes.COMPOUND_EXPRESSION
+        if (hasDynamicTextChild && !isStaticNode(child)) {
+          patchFlag |= PatchFlags.TEXT
         }
-        // patchFlag & dynamicPropNames
-        if (patchFlag !== 0) {
-          if (!hasChildren) {
-            if (!hasProps) {
-              args.push(`null`)
-            }
-            args.push(`null`)
-          }
-          if (__DEV__) {
-            const flagNames = Object.keys(PatchFlagNames)
-              .map(Number)
-              .filter(n => n > 0 && patchFlag & n)
-              .map(n => PatchFlagNames[n])
-              .join(`, `)
-            args.push(patchFlag + ` /* ${flagNames} */`)
-          } else {
-            args.push(patchFlag + '')
-          }
-          if (dynamicPropNames && dynamicPropNames.length) {
-            args.push(
-              `[${dynamicPropNames.map(n => JSON.stringify(n)).join(`, `)}]`
-            )
-          }
+        // pass directly if the only child is a text node
+        // (plain / interpolation / expression)
+        if (hasDynamicTextChild || type === NodeTypes.TEXT) {
+          args.push(child)
+        } else {
+          args.push(node.children)
         }
-
-        const { loc } = node
-        const vnode = createCallExpression(
-          context.helper(CREATE_VNODE),
-          args,
-          loc
+      } else {
+        args.push(node.children)
+      }
+    }
+    // patchFlag & dynamicPropNames
+    if (patchFlag !== 0) {
+      if (!hasChildren) {
+        if (!hasProps) {
+          args.push(`null`)
+        }
+        args.push(`null`)
+      }
+      if (__DEV__) {
+        const flagNames = Object.keys(PatchFlagNames)
+          .map(Number)
+          .filter(n => n > 0 && patchFlag & n)
+          .map(n => PatchFlagNames[n])
+          .join(`, `)
+        args.push(patchFlag + ` /* ${flagNames} */`)
+      } else {
+        args.push(patchFlag + '')
+      }
+      if (dynamicPropNames && dynamicPropNames.length) {
+        args.push(
+          `[${dynamicPropNames.map(n => JSON.stringify(n)).join(`, `)}]`
         )
+      }
+    }
+
+    const { loc } = node
+    const vnode = createCallExpression(context.helper(CREATE_VNODE), args, loc)
 
-        if (runtimeDirectives && runtimeDirectives.length) {
-          node.codegenNode = createCallExpression(
-            context.helper(APPLY_DIRECTIVES),
-            [
-              vnode,
-              createArrayExpression(
-                runtimeDirectives.map(dir => buildDirectiveArgs(dir, context)),
-                loc
-              )
-            ],
+    if (runtimeDirectives && runtimeDirectives.length) {
+      node.codegenNode = createCallExpression(
+        context.helper(APPLY_DIRECTIVES),
+        [
+          vnode,
+          createArrayExpression(
+            runtimeDirectives.map(dir => buildDirectiveArgs(dir, context)),
             loc
           )
-        } else {
-          node.codegenNode = vnode
-        }
-      }
+        ],
+        loc
+      )
+    } else {
+      node.codegenNode = vnode
     }
   }
 }