fix(compiler): generate correct fragment children when it contains single text node or slot outlet
This commit is contained in:
@@ -176,7 +176,7 @@ export type JSChildNode =
|
||||
export interface CallExpression extends Node {
|
||||
type: NodeTypes.JS_CALL_EXPRESSION
|
||||
callee: string
|
||||
arguments: (string | JSChildNode | TemplateChildNode[])[]
|
||||
arguments: (string | JSChildNode | TemplateChildNode | TemplateChildNode[])[]
|
||||
}
|
||||
|
||||
export interface ObjectExpression extends Node {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
RootNode,
|
||||
TemplateChildNode,
|
||||
ElementNode,
|
||||
TextNode,
|
||||
CommentNode,
|
||||
ExpressionNode,
|
||||
@@ -15,7 +14,6 @@ import {
|
||||
InterpolationNode,
|
||||
CompoundExpressionNode,
|
||||
SimpleExpressionNode,
|
||||
ElementTypes,
|
||||
FunctionExpression,
|
||||
SequenceExpression,
|
||||
ConditionalExpression
|
||||
@@ -232,7 +230,8 @@ export function generate(
|
||||
|
||||
// generate the VNode tree expression
|
||||
push(`return `)
|
||||
genChildren(ast.children, context, true)
|
||||
|
||||
genRoot(ast, context)
|
||||
|
||||
if (useWithBlock) {
|
||||
deindent()
|
||||
@@ -257,31 +256,13 @@ function genHoists(hoists: JSChildNode[], context: CodegenContext) {
|
||||
context.newline()
|
||||
}
|
||||
|
||||
// This will generate a single vnode call if:
|
||||
// - The target position explicitly allows a single node (root, if, for)
|
||||
// - The list has length === 1, AND The only child is a:
|
||||
// - text
|
||||
// - <slot> outlet, which always produces an array
|
||||
function genChildren(
|
||||
children: TemplateChildNode[],
|
||||
context: CodegenContext,
|
||||
allowSingle: boolean = false
|
||||
) {
|
||||
if (!children.length) {
|
||||
return context.push(`null`)
|
||||
}
|
||||
const child = children[0]
|
||||
const type = child.type
|
||||
if (
|
||||
children.length === 1 &&
|
||||
(allowSingle ||
|
||||
type === NodeTypes.TEXT ||
|
||||
type === NodeTypes.INTERPOLATION ||
|
||||
type === NodeTypes.COMPOUND_EXPRESSION ||
|
||||
(type === NodeTypes.ELEMENT &&
|
||||
(child as ElementNode).tagType === ElementTypes.SLOT))
|
||||
) {
|
||||
genNode(child, context)
|
||||
function genRoot(root: RootNode, context: CodegenContext) {
|
||||
// TODO handle blocks
|
||||
const { children } = root
|
||||
if (children.length === 0) {
|
||||
context.push(`null`)
|
||||
} else if (children.length === 1) {
|
||||
genNode(children[0], context)
|
||||
} else {
|
||||
genNodeListAsArray(children, context)
|
||||
}
|
||||
@@ -316,7 +297,7 @@ function genNodeList(
|
||||
if (isString(node)) {
|
||||
push(node)
|
||||
} else if (isArray(node)) {
|
||||
genChildren(node, context)
|
||||
genNodeListAsArray(node, context)
|
||||
} else {
|
||||
genNode(node, context)
|
||||
}
|
||||
|
||||
@@ -87,8 +87,26 @@ export const transformElement: NodeTransform = (node, context) => {
|
||||
patchFlag |= PatchFlags.DYNAMIC_SLOTS
|
||||
}
|
||||
} else {
|
||||
// only v-for fragments will have keyed/unkeyed flags
|
||||
args.push(node.children)
|
||||
if (node.children.length === 1) {
|
||||
const child = node.children[0]
|
||||
const type = child.type
|
||||
// pass directly if the only child is one of:
|
||||
// - text (plain / interpolation / expression)
|
||||
// - <slot> outlet (already an array)
|
||||
if (
|
||||
type === NodeTypes.TEXT ||
|
||||
type === NodeTypes.INTERPOLATION ||
|
||||
type === NodeTypes.COMPOUND_EXPRESSION ||
|
||||
(type === NodeTypes.ELEMENT &&
|
||||
(child as ElementNode).tagType === ElementTypes.SLOT)
|
||||
) {
|
||||
args.push(child)
|
||||
} else {
|
||||
args.push(node.children)
|
||||
}
|
||||
} else {
|
||||
args.push(node.children)
|
||||
}
|
||||
}
|
||||
}
|
||||
// patchFlag & dynamicPropNames
|
||||
|
||||
@@ -14,7 +14,9 @@ import {
|
||||
ElementTypes,
|
||||
ObjectExpression,
|
||||
createObjectExpression,
|
||||
createObjectProperty
|
||||
createObjectProperty,
|
||||
TemplateChildNode,
|
||||
CallExpression
|
||||
} from '../ast'
|
||||
import { createCompilerError, ErrorCodes } from '../errors'
|
||||
import { getInnerRange, findProp } from '../utils'
|
||||
@@ -120,12 +122,23 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
)
|
||||
])
|
||||
}
|
||||
let childBlockChildren: TemplateChildNode[] | CallExpression =
|
||||
node.children
|
||||
if (childBlockChildren.length === 1) {
|
||||
const child = childBlockChildren[0]
|
||||
if (
|
||||
child.type === NodeTypes.ELEMENT &&
|
||||
child.tagType === ElementTypes.SLOT
|
||||
) {
|
||||
childBlockChildren = child.codegenNode!
|
||||
}
|
||||
}
|
||||
childBlock = createSequenceExpression([
|
||||
createCallExpression(helper(OPEN_BLOCK)),
|
||||
createCallExpression(helper(CREATE_BLOCK), [
|
||||
helper(FRAGMENT),
|
||||
childBlockProps,
|
||||
node.children
|
||||
childBlockChildren
|
||||
])
|
||||
])
|
||||
} else {
|
||||
|
||||
@@ -164,19 +164,30 @@ function createChildrenCodegenNode(
|
||||
const { children } = branch
|
||||
const child = children[0]
|
||||
const needFragmentWrapper =
|
||||
children.length > 1 || child.type !== NodeTypes.ELEMENT
|
||||
children.length !== 1 ||
|
||||
child.type !== NodeTypes.ELEMENT ||
|
||||
child.tagType === ElementTypes.SLOT
|
||||
if (needFragmentWrapper) {
|
||||
const blockArgs: CallExpression['arguments'] = [
|
||||
helper(FRAGMENT),
|
||||
keyExp,
|
||||
children
|
||||
]
|
||||
// optimize away nested fragments when child is a ForNode
|
||||
if (children.length === 1 && child.type === NodeTypes.FOR) {
|
||||
const forBlockExp = child.codegenNode
|
||||
// directly use the for block's children and patchFlag
|
||||
blockArgs[2] = forBlockExp.arguments[2]
|
||||
blockArgs[3] = forBlockExp.arguments[3]
|
||||
if (children.length === 1) {
|
||||
// optimize away nested fragments when child is a ForNode
|
||||
if (child.type === NodeTypes.FOR) {
|
||||
const forBlockArgs = child.codegenNode.arguments
|
||||
// directly use the for block's children and patchFlag
|
||||
blockArgs[2] = forBlockArgs[2]
|
||||
blockArgs[3] = forBlockArgs[3]
|
||||
} else if (
|
||||
child.type === NodeTypes.ELEMENT &&
|
||||
child.tagType === ElementTypes.SLOT
|
||||
) {
|
||||
// <template v-if="..."><slot/></template>
|
||||
// since slot always returns array, use it directly as the fragment children.
|
||||
blockArgs[2] = child.codegenNode!
|
||||
}
|
||||
}
|
||||
return createCallExpression(helper(CREATE_BLOCK), blockArgs)
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user