feat(compiler-core): support Suspense in templates
This commit is contained in:
parent
e97951dd2e
commit
4b2b29efa1
@ -50,7 +50,8 @@ export const enum ElementTypes {
|
|||||||
COMPONENT,
|
COMPONENT,
|
||||||
SLOT,
|
SLOT,
|
||||||
TEMPLATE,
|
TEMPLATE,
|
||||||
PORTAL
|
PORTAL,
|
||||||
|
SUSPENSE
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Node {
|
export interface Node {
|
||||||
@ -101,6 +102,7 @@ export type ElementNode =
|
|||||||
| SlotOutletNode
|
| SlotOutletNode
|
||||||
| TemplateNode
|
| TemplateNode
|
||||||
| PortalNode
|
| PortalNode
|
||||||
|
| SuspenseNode
|
||||||
|
|
||||||
export interface BaseElementNode extends Node {
|
export interface BaseElementNode extends Node {
|
||||||
type: NodeTypes.ELEMENT
|
type: NodeTypes.ELEMENT
|
||||||
@ -141,6 +143,11 @@ export interface PortalNode extends BaseElementNode {
|
|||||||
codegenNode: ElementCodegenNode | undefined
|
codegenNode: ElementCodegenNode | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SuspenseNode extends BaseElementNode {
|
||||||
|
tagType: ElementTypes.SUSPENSE
|
||||||
|
codegenNode: ElementCodegenNode | undefined
|
||||||
|
}
|
||||||
|
|
||||||
export interface TextNode extends Node {
|
export interface TextNode extends Node {
|
||||||
type: NodeTypes.TEXT
|
type: NodeTypes.TEXT
|
||||||
content: string
|
content: string
|
||||||
|
@ -445,6 +445,8 @@ 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
|
else if (tag === 'portal' || tag === 'Portal') tagType = ElementTypes.PORTAL
|
||||||
|
else if (tag === 'suspense' || tag === 'Suspense')
|
||||||
|
tagType = ElementTypes.SUSPENSE
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -24,7 +24,8 @@ import {
|
|||||||
RESOLVE_COMPONENT,
|
RESOLVE_COMPONENT,
|
||||||
MERGE_PROPS,
|
MERGE_PROPS,
|
||||||
TO_HANDLERS,
|
TO_HANDLERS,
|
||||||
PORTAL
|
PORTAL,
|
||||||
|
SUSPENSE
|
||||||
} from '../runtimeHelpers'
|
} from '../runtimeHelpers'
|
||||||
import { getInnerRange, isVSlot, toValidAssetId } from '../utils'
|
import { getInnerRange, isVSlot, toValidAssetId } from '../utils'
|
||||||
import { buildSlots } from './vSlot'
|
import { buildSlots } from './vSlot'
|
||||||
@ -36,130 +37,126 @@ const directiveImportMap = new WeakMap<DirectiveNode, symbol>()
|
|||||||
|
|
||||||
// generate a JavaScript AST for this element's codegen
|
// generate a JavaScript AST for this element's codegen
|
||||||
export const transformElement: NodeTransform = (node, context) => {
|
export const transformElement: NodeTransform = (node, context) => {
|
||||||
if (node.type === NodeTypes.ELEMENT) {
|
if (
|
||||||
if (
|
node.type !== NodeTypes.ELEMENT ||
|
||||||
node.tagType === ElementTypes.ELEMENT ||
|
// handled by transformSlotOutlet
|
||||||
node.tagType === ElementTypes.COMPONENT ||
|
node.tagType === ElementTypes.SLOT ||
|
||||||
node.tagType === ElementTypes.PORTAL ||
|
// <template v-if/v-for> should have already been replaced
|
||||||
// <template> with v-if or v-for are ignored during traversal.
|
// <templte v-slot> is handled by buildSlots
|
||||||
// <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))
|
) {
|
||||||
) {
|
return
|
||||||
// perform the work on exit, after all child expressions have been
|
}
|
||||||
// processed and merged.
|
// perform the work on exit, after all child expressions have been
|
||||||
return () => {
|
// processed and merged.
|
||||||
const isComponent = node.tagType === ElementTypes.COMPONENT
|
return () => {
|
||||||
const isPortal = node.tagType === ElementTypes.PORTAL
|
const isComponent = node.tagType === ElementTypes.COMPONENT
|
||||||
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
|
||||||
let dynamicPropNames: string[] | undefined
|
let dynamicPropNames: string[] | undefined
|
||||||
|
|
||||||
if (isComponent) {
|
if (isComponent) {
|
||||||
context.helper(RESOLVE_COMPONENT)
|
context.helper(RESOLVE_COMPONENT)
|
||||||
context.components.add(node.tag)
|
context.components.add(node.tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
const args: CallExpression['arguments'] = [
|
const args: CallExpression['arguments'] = [
|
||||||
isComponent
|
isComponent
|
||||||
? toValidAssetId(node.tag, `component`)
|
? toValidAssetId(node.tag, `component`)
|
||||||
: isPortal
|
: node.tagType === ElementTypes.PORTAL
|
||||||
? context.helper(PORTAL)
|
? context.helper(PORTAL)
|
||||||
: `"${node.tag}"`
|
: node.tagType === ElementTypes.SUSPENSE
|
||||||
]
|
? context.helper(SUSPENSE)
|
||||||
// props
|
: `"${node.tag}"`
|
||||||
if (hasProps) {
|
]
|
||||||
const propsBuildResult = buildProps(node, context)
|
// props
|
||||||
patchFlag = propsBuildResult.patchFlag
|
if (hasProps) {
|
||||||
dynamicPropNames = propsBuildResult.dynamicPropNames
|
const propsBuildResult = buildProps(node, context)
|
||||||
runtimeDirectives = propsBuildResult.directives
|
patchFlag = propsBuildResult.patchFlag
|
||||||
if (!propsBuildResult.props) {
|
dynamicPropNames = propsBuildResult.dynamicPropNames
|
||||||
hasProps = false
|
runtimeDirectives = propsBuildResult.directives
|
||||||
} else {
|
if (!propsBuildResult.props) {
|
||||||
args.push(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
|
} else if (node.children.length === 1) {
|
||||||
const hasChildren = node.children.length > 0
|
const child = node.children[0]
|
||||||
if (hasChildren) {
|
const type = child.type
|
||||||
if (!hasProps) {
|
// check for dynamic text children
|
||||||
args.push(`null`)
|
const hasDynamicTextChild =
|
||||||
}
|
type === NodeTypes.INTERPOLATION ||
|
||||||
if (isComponent) {
|
type === NodeTypes.COMPOUND_EXPRESSION
|
||||||
const { slots, hasDynamicSlots } = buildSlots(node, context)
|
if (hasDynamicTextChild && !isStaticNode(child)) {
|
||||||
args.push(slots)
|
patchFlag |= PatchFlags.TEXT
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// patchFlag & dynamicPropNames
|
// pass directly if the only child is a text node
|
||||||
if (patchFlag !== 0) {
|
// (plain / interpolation / expression)
|
||||||
if (!hasChildren) {
|
if (hasDynamicTextChild || type === NodeTypes.TEXT) {
|
||||||
if (!hasProps) {
|
args.push(child)
|
||||||
args.push(`null`)
|
} else {
|
||||||
}
|
args.push(node.children)
|
||||||
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(`, `)}]`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
const { loc } = node
|
args.push(node.children)
|
||||||
const vnode = createCallExpression(
|
}
|
||||||
context.helper(CREATE_VNODE),
|
}
|
||||||
args,
|
// patchFlag & dynamicPropNames
|
||||||
loc
|
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(`, `)}]`
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (runtimeDirectives && runtimeDirectives.length) {
|
const { loc } = node
|
||||||
node.codegenNode = createCallExpression(
|
const vnode = createCallExpression(context.helper(CREATE_VNODE), args, loc)
|
||||||
context.helper(APPLY_DIRECTIVES),
|
|
||||||
[
|
if (runtimeDirectives && runtimeDirectives.length) {
|
||||||
vnode,
|
node.codegenNode = createCallExpression(
|
||||||
createArrayExpression(
|
context.helper(APPLY_DIRECTIVES),
|
||||||
runtimeDirectives.map(dir => buildDirectiveArgs(dir, context)),
|
[
|
||||||
loc
|
vnode,
|
||||||
)
|
createArrayExpression(
|
||||||
],
|
runtimeDirectives.map(dir => buildDirectiveArgs(dir, context)),
|
||||||
loc
|
loc
|
||||||
)
|
)
|
||||||
} else {
|
],
|
||||||
node.codegenNode = vnode
|
loc
|
||||||
}
|
)
|
||||||
}
|
} else {
|
||||||
|
node.codegenNode = vnode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user