diff --git a/packages/compiler-core/src/ast.ts b/packages/compiler-core/src/ast.ts index 0039b6e05..8655a5dc8 100644 --- a/packages/compiler-core/src/ast.ts +++ b/packages/compiler-core/src/ast.ts @@ -1,17 +1,17 @@ import { isString } from '@vue/shared' import { ForParseResult } from './transforms/vFor' import { - CREATE_VNODE, - WITH_DIRECTIVES, RENDER_SLOT, CREATE_SLOTS, RENDER_LIST, OPEN_BLOCK, CREATE_BLOCK, - FRAGMENT + FRAGMENT, + CREATE_VNODE, + WITH_DIRECTIVES } from './runtimeHelpers' import { PropsExpression } from './transforms/transformElement' -import { ImportItem } from './transform' +import { ImportItem, TransformContext } from './transform' // Vue template is a platform-agnostic superset of HTML (syntax only). // More namespaces like SVG and MathML are declared by platform specific @@ -38,12 +38,12 @@ export const enum NodeTypes { FOR, TEXT_CALL, // codegen + VNODE_CALL, JS_CALL_EXPRESSION, JS_OBJECT_EXPRESSION, JS_PROPERTY, JS_ARRAY_EXPRESSION, JS_FUNCTION_EXPRESSION, - JS_SEQUENCE_EXPRESSION, JS_CONDITIONAL_EXPRESSION, JS_CACHE_EXPRESSION, @@ -123,21 +123,14 @@ export interface BaseElementNode extends Node { isSelfClosing: boolean props: Array children: TemplateChildNode[] - codegenNode: - | CallExpression - | SimpleExpressionNode - | CacheExpression - | SequenceExpression - | undefined } export interface PlainElementNode extends BaseElementNode { tagType: ElementTypes.ELEMENT codegenNode: - | ElementCodegenNode + | VNodeCall | SimpleExpressionNode // when hoisted | CacheExpression // when cached by v-once - | SequenceExpression // when turned into a block | undefined ssrCodegenNode?: TemplateLiteral } @@ -145,7 +138,7 @@ export interface PlainElementNode extends BaseElementNode { export interface ComponentNode extends BaseElementNode { tagType: ElementTypes.COMPONENT codegenNode: - | ComponentCodegenNode + | VNodeCall | CacheExpression // when cached by v-once | undefined ssrCodegenNode?: CallExpression @@ -153,13 +146,17 @@ export interface ComponentNode extends BaseElementNode { export interface SlotOutletNode extends BaseElementNode { tagType: ElementTypes.SLOT - codegenNode: SlotOutletCodegenNode | undefined | CacheExpression // when cached by v-once + codegenNode: + | RenderSlotCall + | CacheExpression // when cached by v-once + | undefined ssrCodegenNode?: CallExpression } export interface TemplateNode extends BaseElementNode { tagType: ElementTypes.TEMPLATE // TemplateNode is a container type that always gets compiled away + codegenNode: undefined } export interface TextNode extends Node { @@ -220,7 +217,7 @@ export interface CompoundExpressionNode extends Node { export interface IfNode extends Node { type: NodeTypes.IF branches: IfBranchNode[] - codegenNode?: IfCodegenNode + codegenNode?: IfConditionalExpression } export interface IfBranchNode extends Node { @@ -246,6 +243,28 @@ export interface TextCallNode extends Node { codegenNode: CallExpression | SimpleExpressionNode // when hoisted } +export type TemplateTextChildNode = + | TextNode + | InterpolationNode + | CompoundExpressionNode + +export interface VNodeCall extends Node { + type: NodeTypes.VNODE_CALL + tag: string | symbol | CallExpression + props: PropsExpression | undefined + children: + | TemplateChildNode[] // multiple children + | TemplateTextChildNode // single text child + | SlotsExpression // component slots + | ForRenderListExpression // v-for fragment call + | undefined + patchFlag: string | undefined + dynamicProps: string | undefined + directives: DirectiveArguments | undefined + isBlock: boolean + isForBlock: boolean +} + // JS Node Types --------------------------------------------------------------- // We also include a number of JavaScript AST nodes for code generation. @@ -253,13 +272,13 @@ export interface TextCallNode extends Node { // Vue render function generation. export type JSChildNode = + | VNodeCall | CallExpression | ObjectExpression | ArrayExpression | ExpressionNode | FunctionExpression | ConditionalExpression - | SequenceExpression | CacheExpression | AssignmentExpression @@ -301,11 +320,6 @@ export interface FunctionExpression extends Node { isSlot: boolean } -export interface SequenceExpression extends Node { - type: NodeTypes.JS_SEQUENCE_EXPRESSION - expressions: JSChildNode[] -} - export interface ConditionalExpression extends Node { type: NodeTypes.JS_CONDITIONAL_EXPRESSION test: JSChildNode @@ -360,57 +374,31 @@ export interface ReturnStatement extends Node { // Codegen Node Types ---------------------------------------------------------- -// createVNode(...) -export interface PlainElementCodegenNode extends CallExpression { - callee: typeof CREATE_VNODE | typeof CREATE_BLOCK - arguments: // tag, props, children, patchFlag, dynamicProps - | [string | symbol] - | [string | symbol, PropsExpression] - | [string | symbol, 'null' | PropsExpression, TemplateChildNode[]] - | [ - string | symbol, - 'null' | PropsExpression, - 'null' | TemplateChildNode[], - string - ] - | [ - string | symbol, - 'null' | PropsExpression, - 'null' | TemplateChildNode[], - string, - string - ] +export interface DirectiveArguments extends ArrayExpression { + elements: DirectiveArgumentNode[] } -export type ElementCodegenNode = - | PlainElementCodegenNode - | CodegenNodeWithDirective - -// createVNode(...) -export interface PlainComponentCodegenNode extends CallExpression { - callee: typeof CREATE_VNODE | typeof CREATE_BLOCK - arguments: // Comp, props, slots, patchFlag, dynamicProps - | [string | symbol] - | [string | symbol, PropsExpression] - | [string | symbol, 'null' | PropsExpression, SlotsExpression] - | [ - string | symbol, - 'null' | PropsExpression, - 'null' | SlotsExpression, - string - ] - | [ - string | symbol, - 'null' | PropsExpression, - 'null' | SlotsExpression, - string, - string - ] +export interface DirectiveArgumentNode extends ArrayExpression { + elements: // dir, exp, arg, modifiers + | [string] + | [string, ExpressionNode] + | [string, ExpressionNode, ExpressionNode] + | [string, ExpressionNode, ExpressionNode, ObjectExpression] } -export type ComponentCodegenNode = - | PlainComponentCodegenNode - | CodegenNodeWithDirective +// renderSlot(...) +export interface RenderSlotCall extends CallExpression { + callee: typeof RENDER_SLOT + arguments: // $slots, name, props, fallback + | [string, string | ExpressionNode] + | [string, string | ExpressionNode, PropsExpression] + | [ + string, + string | ExpressionNode, + PropsExpression | '{}', + TemplateChildNode[] + ] +} export type SlotsExpression = SlotsObjectExpression | DynamicSlotsExpression @@ -462,63 +450,20 @@ export interface DynamicSlotFnProperty extends Property { value: SlotFunctionExpression } -// withDirectives(createVNode(...), [ -// [_directive_foo, someValue], -// [_directive_bar, someValue, "arg", { mod: true }] -// ]) -export interface CodegenNodeWithDirective - extends CallExpression { - callee: typeof WITH_DIRECTIVES - arguments: [T, DirectiveArguments] -} - -export interface DirectiveArguments extends ArrayExpression { - elements: DirectiveArgumentNode[] -} - -export interface DirectiveArgumentNode extends ArrayExpression { - elements: // dir, exp, arg, modifiers - | [string] - | [string, ExpressionNode] - | [string, ExpressionNode, ExpressionNode] - | [string, ExpressionNode, ExpressionNode, ObjectExpression] -} - -// renderSlot(...) -export interface SlotOutletCodegenNode extends CallExpression { - callee: typeof RENDER_SLOT - arguments: // $slots, name, props, fallback - | [string, string | ExpressionNode] - | [string, string | ExpressionNode, PropsExpression] - | [ - string, - string | ExpressionNode, - PropsExpression | '{}', - TemplateChildNode[] - ] -} - -export type BlockCodegenNode = - | ElementCodegenNode - | ComponentCodegenNode - | SlotOutletCodegenNode - -export interface IfCodegenNode extends SequenceExpression { - expressions: [OpenBlockExpression, IfConditionalExpression] -} +export type BlockCodegenNode = VNodeCall | RenderSlotCall export interface IfConditionalExpression extends ConditionalExpression { consequent: BlockCodegenNode alternate: BlockCodegenNode | IfConditionalExpression } -export interface ForCodegenNode extends SequenceExpression { - expressions: [OpenBlockExpression, ForBlockCodegenNode] -} - -export interface ForBlockCodegenNode extends CallExpression { - callee: typeof CREATE_BLOCK - arguments: [typeof FRAGMENT, 'null', ForRenderListExpression, string] +export interface ForCodegenNode extends VNodeCall { + isBlock: true + tag: typeof FRAGMENT + props: undefined + children: ForRenderListExpression + patchFlag: string + isForBlock: true } export interface ForRenderListExpression extends CallExpression { @@ -530,11 +475,6 @@ export interface ForIteratorExpression extends FunctionExpression { returns: BlockCodegenNode } -export interface OpenBlockExpression extends CallExpression { - callee: typeof OPEN_BLOCK - arguments: [] -} - // AST Utilities --------------------------------------------------------------- // Some expressions, e.g. sequence and conditional expressions, are never @@ -565,6 +505,42 @@ export function createRoot( } } +export function createVNodeCall( + context: TransformContext, + tag: VNodeCall['tag'], + props?: VNodeCall['props'], + children?: VNodeCall['children'], + patchFlag?: VNodeCall['patchFlag'], + dynamicProps?: VNodeCall['dynamicProps'], + directives?: VNodeCall['directives'], + isBlock: VNodeCall['isBlock'] = false, + isForBlock: VNodeCall['isForBlock'] = false, + loc = locStub +): VNodeCall { + if (isBlock) { + context.helper(OPEN_BLOCK) + context.helper(CREATE_BLOCK) + } else { + context.helper(CREATE_VNODE) + } + if (directives) { + context.helper(WITH_DIRECTIVES) + } + + return { + type: NodeTypes.VNODE_CALL, + tag, + props, + children, + patchFlag, + dynamicProps, + directives, + isBlock, + isForBlock, + loc + } +} + export function createArrayExpression( elements: ArrayExpression['elements'], loc: SourceLocation = locStub @@ -638,15 +614,9 @@ export function createCompoundExpression( } } -type InferCodegenNodeType = T extends - | typeof CREATE_VNODE - | typeof CREATE_BLOCK - ? PlainElementCodegenNode | PlainComponentCodegenNode - : T extends typeof WITH_DIRECTIVES - ? - | CodegenNodeWithDirective - | CodegenNodeWithDirective - : T extends typeof RENDER_SLOT ? SlotOutletCodegenNode : CallExpression +type InferCodegenNodeType = T extends typeof RENDER_SLOT + ? RenderSlotCall + : CallExpression export function createCallExpression( callee: T, @@ -678,16 +648,6 @@ export function createFunctionExpression( } } -export function createSequenceExpression( - expressions: SequenceExpression['expressions'] -): SequenceExpression { - return { - type: NodeTypes.JS_SEQUENCE_EXPRESSION, - expressions, - loc: locStub - } -} - export function createConditionalExpression( test: ConditionalExpression['test'], consequent: ConditionalExpression['consequent'], diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts index 1a88da5e2..8014416f2 100644 --- a/packages/compiler-core/src/codegen.ts +++ b/packages/compiler-core/src/codegen.ts @@ -15,7 +15,6 @@ import { CompoundExpressionNode, SimpleExpressionNode, FunctionExpression, - SequenceExpression, ConditionalExpression, CacheExpression, locStub, @@ -23,7 +22,8 @@ import { TemplateLiteral, IfStatement, AssignmentExpression, - ReturnStatement + ReturnStatement, + VNodeCall } from './ast' import { SourceMapGenerator, RawSourceMap } from 'source-map' import { @@ -45,7 +45,10 @@ import { CREATE_TEXT, PUSH_SCOPE_ID, POP_SCOPE_ID, - WITH_SCOPE_ID + WITH_SCOPE_ID, + WITH_DIRECTIVES, + CREATE_BLOCK, + OPEN_BLOCK } from './runtimeHelpers' import { ImportItem } from './transform' @@ -547,6 +550,10 @@ function genNode(node: CodegenNode | symbol | string, context: CodegenContext) { case NodeTypes.COMMENT: genComment(node, context) break + case NodeTypes.VNODE_CALL: + genVNodeCall(node, context) + break + case NodeTypes.JS_CALL_EXPRESSION: genCallExpression(node, context) break @@ -559,9 +566,6 @@ function genNode(node: CodegenNode | symbol | string, context: CodegenContext) { case NodeTypes.JS_FUNCTION_EXPRESSION: genFunctionExpression(node, context) break - case NodeTypes.JS_SEQUENCE_EXPRESSION: - genSequenceExpression(node, context) - break case NodeTypes.JS_CONDITIONAL_EXPRESSION: genConditionalExpression(node, context) break @@ -657,6 +661,48 @@ function genComment(node: CommentNode, context: CodegenContext) { } } +function genVNodeCall(node: VNodeCall, context: CodegenContext) { + const { push, helper } = context + const { + tag, + props, + children, + patchFlag, + dynamicProps, + directives, + isBlock, + isForBlock + } = node + if (directives) { + push(helper(WITH_DIRECTIVES) + `(`) + } + if (isBlock) { + push(`(${helper(OPEN_BLOCK)}(${isForBlock ? `true` : ``}), `) + } + push(helper(isBlock ? CREATE_BLOCK : CREATE_VNODE) + `(`, node) + genNodeList( + genNullableArgs([tag, props, children, patchFlag, dynamicProps]), + context + ) + push(`)`) + if (isBlock) { + push(`)`) + } + if (directives) { + push(`, `) + genNode(directives, context) + push(`)`) + } +} + +function genNullableArgs(args: any[]): CallExpression['arguments'] { + let i = args.length + while (i--) { + if (args[i] != null) break + } + return args.slice(0, i + 1).map(arg => arg || `null`) +} + // JavaScript function genCallExpression(node: CallExpression, context: CodegenContext) { const callee = isString(node.callee) @@ -782,15 +828,6 @@ function genConditionalExpression( needNewline && deindent(true /* without newline */) } -function genSequenceExpression( - node: SequenceExpression, - context: CodegenContext -) { - context.push(`(`) - genNodeList(node.expressions, context) - context.push(`)`) -} - function genCacheExpression(node: CacheExpression, context: CodegenContext) { const { push, helper, indent, deindent, newline } = context push(`_cache[${node.index}] || (`) diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts index ee8f98f6c..758dc8357 100644 --- a/packages/compiler-core/src/transform.ts +++ b/packages/compiler-core/src/transform.ts @@ -12,12 +12,10 @@ import { JSChildNode, SimpleExpressionNode, ElementTypes, - ElementCodegenNode, - ComponentCodegenNode, - createCallExpression, CacheExpression, createCacheExpression, - TemplateLiteral + TemplateLiteral, + createVNodeCall } from './ast' import { isString, @@ -31,11 +29,11 @@ import { TO_DISPLAY_STRING, FRAGMENT, helperNameMap, - WITH_DIRECTIVES, CREATE_BLOCK, - CREATE_COMMENT + CREATE_COMMENT, + OPEN_BLOCK } from './runtimeHelpers' -import { isVSlot, createBlockExpression } from './utils' +import { isVSlot } from './utils' import { hoistStatic, isSingleElementRoot } from './transforms/hoistStatic' // There are two types of transforms: @@ -286,20 +284,13 @@ function createRootCodegen(root: RootNode, context: TransformContext) { if (isSingleElementRoot(root, child) && child.codegenNode) { // single element root is never hoisted so codegenNode will never be // SimpleExpressionNode - const codegenNode = child.codegenNode as - | ElementCodegenNode - | ComponentCodegenNode - | CacheExpression - if (codegenNode.type !== NodeTypes.JS_CACHE_EXPRESSION) { - if (codegenNode.callee === WITH_DIRECTIVES) { - codegenNode.arguments[0].callee = helper(CREATE_BLOCK) - } else { - codegenNode.callee = helper(CREATE_BLOCK) - } - root.codegenNode = createBlockExpression(codegenNode, context) - } else { - root.codegenNode = codegenNode + const codegenNode = child.codegenNode + if (codegenNode.type === NodeTypes.VNODE_CALL) { + codegenNode.isBlock = true + helper(OPEN_BLOCK) + helper(CREATE_BLOCK) } + root.codegenNode = codegenNode } else { // - single , IfNode, ForNode: already blocks. // - single text node: always patched. @@ -308,16 +299,17 @@ function createRootCodegen(root: RootNode, context: TransformContext) { } } else if (children.length > 1) { // root has multiple nodes - return a fragment block. - root.codegenNode = createBlockExpression( - createCallExpression(helper(CREATE_BLOCK), [ - helper(FRAGMENT), - `null`, - root.children, - `${PatchFlags.STABLE_FRAGMENT} /* ${ - PatchFlagNames[PatchFlags.STABLE_FRAGMENT] - } */` - ]), - context + root.codegenNode = createVNodeCall( + context, + helper(FRAGMENT), + undefined, + root.children, + `${PatchFlags.STABLE_FRAGMENT} /* ${ + PatchFlagNames[PatchFlags.STABLE_FRAGMENT] + } */`, + undefined, + undefined, + true ) } else { // no children = noop. codegen will return null. diff --git a/packages/compiler-core/src/transforms/hoistStatic.ts b/packages/compiler-core/src/transforms/hoistStatic.ts index f04f24449..2dd514bee 100644 --- a/packages/compiler-core/src/transforms/hoistStatic.ts +++ b/packages/compiler-core/src/transforms/hoistStatic.ts @@ -8,11 +8,9 @@ import { ComponentNode, TemplateNode, ElementNode, - PlainElementCodegenNode, - CodegenNodeWithDirective + VNodeCall } from '../ast' import { TransformContext } from '../transform' -import { WITH_DIRECTIVES } from '../runtimeHelpers' import { PatchFlags, isString, isSymbol } from '@vue/shared' import { isSlotOutlet, findProp } from '../utils' @@ -60,7 +58,7 @@ function walk( // node may contain dynamic children, but its props may be eligible for // hoisting. const codegenNode = child.codegenNode! - if (codegenNode.type === NodeTypes.JS_CALL_EXPRESSION) { + if (codegenNode.type === NodeTypes.VNODE_CALL) { const flag = getPatchFlag(codegenNode) if ( (!flag || @@ -70,8 +68,8 @@ function walk( !hasCachedProps(child) ) { const props = getNodeProps(child) - if (props && props !== `null`) { - getVNodeCall(codegenNode).arguments[1] = context.hoist(props) + if (props) { + codegenNode.props = context.hoist(props) } } } @@ -111,7 +109,7 @@ export function isStaticNode( return cached } const codegenNode = node.codegenNode! - if (codegenNode.type !== NodeTypes.JS_CALL_EXPRESSION) { + if (codegenNode.type !== NodeTypes.VNODE_CALL) { return false } const flag = getPatchFlag(codegenNode) @@ -123,6 +121,12 @@ export function isStaticNode( return false } } + // only svg/foeignObject could be block here, however if they are static + // then they don't need to be blocks since there will be no nested + // udpates. + if (codegenNode.isBlock) { + codegenNode.isBlock = false + } resultCache.set(node, true) return true } else { @@ -164,11 +168,7 @@ function hasCachedProps(node: PlainElementNode): boolean { return false } const props = getNodeProps(node) - if ( - props && - props !== 'null' && - props.type === NodeTypes.JS_OBJECT_EXPRESSION - ) { + if (props && props.type === NodeTypes.JS_OBJECT_EXPRESSION) { const { properties } = props for (let i = 0; i < properties.length; i++) { if (properties[i].value.type === NodeTypes.JS_CACHE_EXPRESSION) { @@ -181,30 +181,12 @@ function hasCachedProps(node: PlainElementNode): boolean { function getNodeProps(node: PlainElementNode) { const codegenNode = node.codegenNode! - if (codegenNode.type === NodeTypes.JS_CALL_EXPRESSION) { - return getVNodeArgAt( - codegenNode, - 1 - ) as PlainElementCodegenNode['arguments'][1] + if (codegenNode.type === NodeTypes.VNODE_CALL) { + return codegenNode.props } } -type NonCachedCodegenNode = - | PlainElementCodegenNode - | CodegenNodeWithDirective - -function getVNodeArgAt( - node: NonCachedCodegenNode, - index: number -): PlainElementCodegenNode['arguments'][number] { - return getVNodeCall(node).arguments[index] -} - -function getVNodeCall(node: NonCachedCodegenNode) { - return node.callee === WITH_DIRECTIVES ? node.arguments[0] : node -} - -function getPatchFlag(node: NonCachedCodegenNode): number | undefined { - const flag = getVNodeArgAt(node, 3) as string +function getPatchFlag(node: VNodeCall): number | undefined { + const flag = node.patchFlag return flag ? parseInt(flag, 10) : undefined } diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts index f12f22049..2d8eead62 100644 --- a/packages/compiler-core/src/transforms/transformElement.ts +++ b/packages/compiler-core/src/transforms/transformElement.ts @@ -14,23 +14,22 @@ import { createSimpleExpression, createObjectExpression, Property, - createSequenceExpression, - ComponentNode + ComponentNode, + VNodeCall, + TemplateTextChildNode, + DirectiveArguments, + createVNodeCall } from '../ast' import { PatchFlags, PatchFlagNames, isSymbol } from '@vue/shared' import { createCompilerError, ErrorCodes } from '../errors' import { - CREATE_VNODE, - WITH_DIRECTIVES, RESOLVE_DIRECTIVE, RESOLVE_COMPONENT, RESOLVE_DYNAMIC_COMPONENT, MERGE_PROPS, TO_HANDLERS, PORTAL, - KEEP_ALIVE, - OPEN_BLOCK, - CREATE_BLOCK + KEEP_ALIVE } from '../runtimeHelpers' import { getInnerRange, @@ -63,6 +62,20 @@ export const transformElement: NodeTransform = (node, context) => { const { tag, props } = node const isComponent = node.tagType === ElementTypes.COMPONENT + // The goal of the transform is to create a codegenNode implementing the + // VNodeCall interface. + const vnodeTag = isComponent + ? resolveComponentType(node as ComponentNode, context) + : `"${tag}"` + + let vnodeProps: VNodeCall['props'] + let vnodeChildren: VNodeCall['children'] + let vnodePatchFlag: VNodeCall['patchFlag'] + let patchFlag: number = 0 + let vnodeDynamicProps: VNodeCall['dynamicProps'] + let dynamicPropNames: string[] | undefined + let vnodeDirectives: VNodeCall['directives'] + // and must be forced into blocks so that block // updates inside get proper isSVG flag at runtime. (#639, #643) // This is technically web-specific, but splitting the logic out of core @@ -70,38 +83,24 @@ export const transformElement: NodeTransform = (node, context) => { let shouldUseBlock = !isComponent && (tag === 'svg' || tag === 'foreignObject') - const nodeType = isComponent - ? resolveComponentType(node as ComponentNode, context) - : `"${tag}"` - - const args: CallExpression['arguments'] = [nodeType] - - let hasProps = props.length > 0 - let patchFlag: number = 0 - let runtimeDirectives: DirectiveNode[] | undefined - let dynamicPropNames: string[] | undefined - // props - if (hasProps) { + if (props.length > 0) { const propsBuildResult = buildProps(node, context) + vnodeProps = propsBuildResult.props patchFlag = propsBuildResult.patchFlag dynamicPropNames = propsBuildResult.dynamicPropNames - runtimeDirectives = propsBuildResult.directives - if (!propsBuildResult.props) { - hasProps = false - } else { - args.push(propsBuildResult.props) - } + const directives = propsBuildResult.directives + vnodeDirectives = + directives && directives.length + ? (createArrayExpression( + directives.map(dir => buildDirectiveArgs(dir, context)) + ) as DirectiveArguments) + : undefined } // children - const hasChildren = node.children.length > 0 - if (hasChildren) { - if (!hasProps) { - args.push(`null`) - } - - if (nodeType === KEEP_ALIVE) { + if (node.children.length > 0) { + if (vnodeTag === KEEP_ALIVE) { // Although a built-in component, we compile KeepAlive with raw children // instead of slot functions so that it can be used inside Transition // or other Transition-wrapping HOCs. @@ -125,13 +124,13 @@ export const transformElement: NodeTransform = (node, context) => { const shouldBuildAsSlots = isComponent && // Portal is not a real component has dedicated handling in the renderer - nodeType !== PORTAL && + vnodeTag !== PORTAL && // explained above. - nodeType !== KEEP_ALIVE + vnodeTag !== KEEP_ALIVE if (shouldBuildAsSlots) { const { slots, hasDynamicSlots } = buildSlots(node, context) - args.push(slots) + vnodeChildren = slots if (hasDynamicSlots) { patchFlag |= PatchFlags.DYNAMIC_SLOTS } @@ -148,60 +147,44 @@ export const transformElement: NodeTransform = (node, context) => { // pass directly if the only child is a text node // (plain / interpolation / expression) if (hasDynamicTextChild || type === NodeTypes.TEXT) { - args.push(child) + vnodeChildren = child as TemplateTextChildNode } else { - args.push(node.children) + vnodeChildren = node.children } } else { - args.push(node.children) + vnodeChildren = 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} */`) + vnodePatchFlag = patchFlag + ` /* ${flagNames} */` } else { - args.push(patchFlag + '') + vnodePatchFlag = String(patchFlag) } if (dynamicPropNames && dynamicPropNames.length) { - args.push(stringifyDynamicPropNames(dynamicPropNames)) + vnodeDynamicProps = stringifyDynamicPropNames(dynamicPropNames) } } - const { loc } = node - const vnode = shouldUseBlock - ? createSequenceExpression([ - createCallExpression(context.helper(OPEN_BLOCK)), - createCallExpression(context.helper(CREATE_BLOCK), args, loc) - ]) - : createCallExpression(context.helper(CREATE_VNODE), args, loc) - if (runtimeDirectives && runtimeDirectives.length) { - node.codegenNode = createCallExpression( - context.helper(WITH_DIRECTIVES), - [ - vnode, - createArrayExpression( - runtimeDirectives.map(dir => buildDirectiveArgs(dir, context)), - loc - ) - ], - loc - ) - } else { - node.codegenNode = vnode - } + node.codegenNode = createVNodeCall( + context, + vnodeTag, + vnodeProps, + vnodeChildren, + vnodePatchFlag, + vnodeDynamicProps, + vnodeDirectives, + shouldUseBlock, + false /* isForBlock */, + node.loc + ) } } diff --git a/packages/compiler-core/src/transforms/vFor.ts b/packages/compiler-core/src/transforms/vFor.ts index 48b2e3d1e..a1869ba7f 100644 --- a/packages/compiler-core/src/transforms/vFor.ts +++ b/packages/compiler-core/src/transforms/vFor.ts @@ -8,26 +8,28 @@ import { createSimpleExpression, SourceLocation, SimpleExpressionNode, - createSequenceExpression, createCallExpression, createFunctionExpression, ElementTypes, createObjectExpression, createObjectProperty, ForCodegenNode, - ElementCodegenNode, - SlotOutletCodegenNode, + RenderSlotCall, SlotOutletNode, ElementNode, DirectiveNode, ForNode, - PlainElementNode + PlainElementNode, + createVNodeCall, + VNodeCall, + ForRenderListExpression, + BlockCodegenNode, + ForIteratorExpression } from '../ast' import { createCompilerError, ErrorCodes } from '../errors' import { getInnerRange, findProp, - createBlockExpression, isTemplateNode, isSlotOutlet, injectProp @@ -36,8 +38,7 @@ import { RENDER_LIST, OPEN_BLOCK, CREATE_BLOCK, - FRAGMENT, - WITH_DIRECTIVES + FRAGMENT } from '../runtimeHelpers' import { processExpression } from './transformExpression' import { PatchFlags, PatchFlagNames } from '@vue/shared' @@ -51,26 +52,27 @@ export const transformFor = createStructuralDirectiveTransform( // iterator on exit after all children have been traversed const renderExp = createCallExpression(helper(RENDER_LIST), [ forNode.source - ]) + ]) as ForRenderListExpression const keyProp = findProp(node, `key`) const fragmentFlag = keyProp ? PatchFlags.KEYED_FRAGMENT : PatchFlags.UNKEYED_FRAGMENT - forNode.codegenNode = createSequenceExpression([ - // v-for fragment blocks disable tracking since they always diff their - // children - createCallExpression(helper(OPEN_BLOCK), [`true`]), - createCallExpression(helper(CREATE_BLOCK), [ - helper(FRAGMENT), - `null`, - renderExp, - `${fragmentFlag} /* ${PatchFlagNames[fragmentFlag]} */` - ]) - ]) as ForCodegenNode + forNode.codegenNode = createVNodeCall( + context, + helper(FRAGMENT), + undefined, + renderExp, + `${fragmentFlag} /* ${PatchFlagNames[fragmentFlag]} */`, + undefined, + undefined, + true /* isBlock */, + true /* isForBlock */, + node.loc + ) as ForCodegenNode return () => { // finish the codegen now that all children have been traversed - let childBlock + let childBlock: BlockCodegenNode const isTemplate = isTemplateNode(node) const { children } = forNode const needFragmentWrapper = @@ -92,7 +94,7 @@ export const transformFor = createStructuralDirectiveTransform( : null if (slotOutlet) { // or - childBlock = slotOutlet.codegenNode as SlotOutletCodegenNode + childBlock = slotOutlet.codegenNode as RenderSlotCall if (isTemplate && keyProperty) { // // we need to inject the key to the renderSlot() call. @@ -102,37 +104,33 @@ export const transformFor = createStructuralDirectiveTransform( } else if (needFragmentWrapper) { //