feat(compiler-core): support <portal> in template (#203)

This commit is contained in:
terencez 2019-10-15 03:11:04 +08:00 committed by Evan You
parent 37cbd0098d
commit 4547d85a38
4 changed files with 66 additions and 4 deletions

View File

@ -6,7 +6,8 @@ import {
RESOLVE_DIRECTIVE, RESOLVE_DIRECTIVE,
APPLY_DIRECTIVES, APPLY_DIRECTIVES,
TO_HANDLERS, TO_HANDLERS,
helperNameMap helperNameMap,
PORTAL
} from '../../src/runtimeHelpers' } from '../../src/runtimeHelpers'
import { import {
CallExpression, 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', () => { test('error on v-bind with no argument', () => {
const onError = jest.fn() const onError = jest.fn()
parseWithElementTransform(`<div v-bind/>`, { onError }) parseWithElementTransform(`<div v-bind/>`, { onError })

View File

@ -49,7 +49,8 @@ export const enum ElementTypes {
ELEMENT, ELEMENT,
COMPONENT, COMPONENT,
SLOT, SLOT,
TEMPLATE TEMPLATE,
PORTAL
} }
export interface Node { export interface Node {
@ -99,6 +100,7 @@ export type ElementNode =
| ComponentNode | ComponentNode
| SlotOutletNode | SlotOutletNode
| TemplateNode | TemplateNode
| PortalNode
export interface BaseElementNode extends Node { export interface BaseElementNode extends Node {
type: NodeTypes.ELEMENT type: NodeTypes.ELEMENT
@ -134,6 +136,11 @@ export interface TemplateNode extends BaseElementNode {
| undefined | undefined
} }
export interface PortalNode extends BaseElementNode {
tagType: ElementTypes.PORTAL
codegenNode: ElementCodegenNode | undefined
}
export interface TextNode extends Node { export interface TextNode extends Node {
type: NodeTypes.TEXT type: NodeTypes.TEXT
content: string content: string

View File

@ -445,6 +445,7 @@ function parseTag(
if (tag === 'slot') tagType = ElementTypes.SLOT if (tag === 'slot') tagType = ElementTypes.SLOT
else if (tag === 'template') tagType = ElementTypes.TEMPLATE else if (tag === 'template') tagType = ElementTypes.TEMPLATE
else if (tag === 'portal' || tag === 'Portal') tagType = ElementTypes.PORTAL
} }
return { return {

View File

@ -23,7 +23,8 @@ import {
RESOLVE_DIRECTIVE, RESOLVE_DIRECTIVE,
RESOLVE_COMPONENT, RESOLVE_COMPONENT,
MERGE_PROPS, MERGE_PROPS,
TO_HANDLERS TO_HANDLERS,
PORTAL
} from '../runtimeHelpers' } from '../runtimeHelpers'
import { getInnerRange, isVSlot, toValidAssetId } from '../utils' import { getInnerRange, isVSlot, toValidAssetId } from '../utils'
import { buildSlots } from './vSlot' import { buildSlots } from './vSlot'
@ -38,6 +39,7 @@ export const transformElement: NodeTransform = (node, context) => {
if ( if (
node.tagType === ElementTypes.ELEMENT || node.tagType === ElementTypes.ELEMENT ||
node.tagType === ElementTypes.COMPONENT || node.tagType === ElementTypes.COMPONENT ||
node.tagType === ElementTypes.PORTAL ||
// <template> with v-if or v-for are ignored during traversal. // <template> with v-if or v-for are ignored during traversal.
// <template> without v-slot should be treated as a normal element. // <template> without v-slot should be treated as a normal element.
(node.tagType === ElementTypes.TEMPLATE && !node.props.some(isVSlot)) (node.tagType === ElementTypes.TEMPLATE && !node.props.some(isVSlot))
@ -46,6 +48,7 @@ export const transformElement: NodeTransform = (node, context) => {
// processed and merged. // processed and merged.
return () => { return () => {
const isComponent = node.tagType === ElementTypes.COMPONENT const isComponent = node.tagType === ElementTypes.COMPONENT
const isPortal = node.tagType === ElementTypes.PORTAL
let hasProps = node.props.length > 0 let hasProps = node.props.length > 0
let patchFlag: number = 0 let patchFlag: number = 0
let runtimeDirectives: DirectiveNode[] | undefined let runtimeDirectives: DirectiveNode[] | undefined
@ -57,7 +60,11 @@ export const transformElement: NodeTransform = (node, context) => {
} }
const args: CallExpression['arguments'] = [ const args: CallExpression['arguments'] = [
isComponent ? toValidAssetId(node.tag, `component`) : `"${node.tag}"` isComponent
? toValidAssetId(node.tag, `component`)
: isPortal
? context.helper(PORTAL)
: `"${node.tag}"`
] ]
// props // props
if (hasProps) { if (hasProps) {