]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler-core): support <portal> in template (#203)
authorterencez <texvnars@gmail.com>
Mon, 14 Oct 2019 19:11:04 +0000 (03:11 +0800)
committerEvan You <yyx990803@gmail.com>
Mon, 14 Oct 2019 19:11:04 +0000 (15:11 -0400)
packages/compiler-core/__tests__/transforms/transformElement.spec.ts
packages/compiler-core/src/ast.ts
packages/compiler-core/src/parse.ts
packages/compiler-core/src/transforms/transformElement.ts

index ae44be6847c989648c05340d5884135a7de94905..8f803640a80cc1db9fee771debd2dc72a4782fdb 100644 (file)
@@ -6,7 +6,8 @@ import {
   RESOLVE_DIRECTIVE,
   APPLY_DIRECTIVES,
   TO_HANDLERS,
-  helperNameMap
+  helperNameMap,
+  PORTAL
 } from '../../src/runtimeHelpers'
 import {
   CallExpression,
@@ -255,6 +256,52 @@ describe('compiler: element transform', () => {
     ])
   })
 
+  test('should handle <portal> element', () => {
+    const { node } = parseWithElementTransform(
+      `<portal target="#foo"><span /></portal>`
+    )
+    expect(node.callee).toBe(CREATE_VNODE)
+    expect(node.arguments).toMatchObject([
+      PORTAL,
+      createObjectMatcher({
+        target: '#foo'
+      }),
+      [
+        {
+          type: NodeTypes.ELEMENT,
+          tag: 'span',
+          codegenNode: {
+            callee: CREATE_VNODE,
+            arguments: [`"span"`]
+          }
+        }
+      ]
+    ])
+  })
+
+  test('should handle <Portal> element', () => {
+    const { node } = parseWithElementTransform(
+      `<Portal target="#foo"><span /></Portal>`
+    )
+    expect(node.callee).toBe(CREATE_VNODE)
+    expect(node.arguments).toMatchObject([
+      PORTAL,
+      createObjectMatcher({
+        target: '#foo'
+      }),
+      [
+        {
+          type: NodeTypes.ELEMENT,
+          tag: 'span',
+          codegenNode: {
+            callee: CREATE_VNODE,
+            arguments: [`"span"`]
+          }
+        }
+      ]
+    ])
+  })
+
   test('error on v-bind with no argument', () => {
     const onError = jest.fn()
     parseWithElementTransform(`<div v-bind/>`, { onError })
index b6442202dfe49cf503355333ebeb09cbf07b7b4f..559167659d21aaadaffc835f417c52b56bd7ec6e 100644 (file)
@@ -49,7 +49,8 @@ export const enum ElementTypes {
   ELEMENT,
   COMPONENT,
   SLOT,
-  TEMPLATE
+  TEMPLATE,
+  PORTAL
 }
 
 export interface Node {
@@ -99,6 +100,7 @@ export type ElementNode =
   | ComponentNode
   | SlotOutletNode
   | TemplateNode
+  | PortalNode
 
 export interface BaseElementNode extends Node {
   type: NodeTypes.ELEMENT
@@ -134,6 +136,11 @@ export interface TemplateNode extends BaseElementNode {
     | undefined
 }
 
+export interface PortalNode extends BaseElementNode {
+  tagType: ElementTypes.PORTAL
+  codegenNode: ElementCodegenNode | undefined
+}
+
 export interface TextNode extends Node {
   type: NodeTypes.TEXT
   content: string
index 1e8a6d2a81accae13bd345fbd31212e08c299619..323d46e0806fc4c6f7afdf8717fda50dc0cb4442 100644 (file)
@@ -445,6 +445,7 @@ function parseTag(
 
     if (tag === 'slot') tagType = ElementTypes.SLOT
     else if (tag === 'template') tagType = ElementTypes.TEMPLATE
+    else if (tag === 'portal' || tag === 'Portal') tagType = ElementTypes.PORTAL
   }
 
   return {
index 3df966a8f2cf82ddd2885d99ba0d796b93bd2161..dd0f2d0e07be1c37267dcfd5f2ba848b568fc952 100644 (file)
@@ -23,7 +23,8 @@ import {
   RESOLVE_DIRECTIVE,
   RESOLVE_COMPONENT,
   MERGE_PROPS,
-  TO_HANDLERS
+  TO_HANDLERS,
+  PORTAL
 } from '../runtimeHelpers'
 import { getInnerRange, isVSlot, toValidAssetId } from '../utils'
 import { buildSlots } from './vSlot'
@@ -38,6 +39,7 @@ export const transformElement: NodeTransform = (node, context) => {
     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))
@@ -46,6 +48,7 @@ export const transformElement: NodeTransform = (node, context) => {
       // 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
@@ -57,7 +60,11 @@ export const transformElement: NodeTransform = (node, context) => {
         }
 
         const args: CallExpression['arguments'] = [
-          isComponent ? toValidAssetId(node.tag, `component`) : `"${node.tag}"`
+          isComponent
+            ? toValidAssetId(node.tag, `component`)
+            : isPortal
+              ? context.helper(PORTAL)
+              : `"${node.tag}"`
         ]
         // props
         if (hasProps) {