diff --git a/packages/compiler-dom/src/transforms/stringifyStatic.ts b/packages/compiler-dom/src/transforms/stringifyStatic.ts index ad5ad616e9e..a48dabcbdd4 100644 --- a/packages/compiler-dom/src/transforms/stringifyStatic.ts +++ b/packages/compiler-dom/src/transforms/stringifyStatic.ts @@ -13,7 +13,8 @@ import { ExpressionNode, ElementTypes, PlainElementNode, - JSChildNode + JSChildNode, + TextCallNode } from '@vue/compiler-core' import { isVoidTag, @@ -32,13 +33,15 @@ export const enum StringifyThresholds { NODE_COUNT = 20 } +type StringiableNode = PlainElementNode | TextCallNode + // Turn eligible hoisted static trees into stringied static nodes, e.g. // const _hoisted_1 = createStaticVNode(`
bar
`) // This is only performed in non-in-browser compilations. export const stringifyStatic: HoistTransform = (children, context) => { let nc = 0 // current node count let ec = 0 // current element with binding count - const currentChunk: PlainElementNode[] = [] + const currentChunk: StringiableNode[] = [] const stringifyCurrentChunk = (currentIndex: number): number => { if ( @@ -48,7 +51,7 @@ export const stringifyStatic: HoistTransform = (children, context) => { // combine all currently eligible nodes into a single static vnode call const staticCall = createCallExpression(context.helper(CREATE_STATIC), [ JSON.stringify( - currentChunk.map(node => stringifyElement(node, context)).join('') + currentChunk.map(node => stringifyNode(node, context)).join('') ), // the 2nd argument indicates the number of DOM nodes this static vnode // will insert / hydrate @@ -77,8 +80,8 @@ export const stringifyStatic: HoistTransform = (children, context) => { const child = children[i] const hoisted = getHoistedNode(child) if (hoisted) { - // presence of hoisted means child must be a plain element Node - const node = child as PlainElementNode + // presence of hoisted means child must be a stringifiable node + const node = child as StringiableNode const result = analyzeNode(node) if (result) { // node is stringifiable, record state @@ -102,8 +105,8 @@ export const stringifyStatic: HoistTransform = (children, context) => { } const getHoistedNode = (node: TemplateChildNode) => - node.type === NodeTypes.ELEMENT && - node.tagType === ElementTypes.ELEMENT && + ((node.type === NodeTypes.ELEMENT && node.tagType === ElementTypes.ELEMENT) || + node.type == NodeTypes.TEXT_CALL) && node.codegenNode && node.codegenNode.type === NodeTypes.SIMPLE_EXPRESSION && node.codegenNode.hoisted @@ -114,7 +117,7 @@ const isStringifiableAttr = (name: string) => { } const replaceHoist = ( - node: PlainElementNode, + node: StringiableNode, replacement: JSChildNode | null, context: TransformContext ) => { @@ -125,11 +128,15 @@ const replaceHoist = ( /** * for a hoisted node, analyze it and return: * - false: bailed (contains runtime constant) - * - [x, y] where - * - x is the number of nodes inside - * - y is the number of element with bindings inside + * - [nc, ec] where + * - nc is the number of nodes inside + * - ec is the number of element with bindings inside */ -function analyzeNode(node: PlainElementNode): [number, number] | false { +function analyzeNode(node: StringiableNode): [number, number] | false { + if (node.type === NodeTypes.TEXT_CALL) { + return [1, 0] + } + let nc = 1 // node count let ec = node.props.length > 0 ? 1 : 0 // element w/ binding count let bailed = false @@ -196,6 +203,35 @@ function analyzeNode(node: PlainElementNode): [number, number] | false { return walk(node) ? [nc, ec] : false } +function stringifyNode( + node: string | TemplateChildNode, + context: TransformContext +): string { + if (isString(node)) { + return node + } + if (isSymbol(node)) { + return `` + } + switch (node.type) { + case NodeTypes.ELEMENT: + return stringifyElement(node, context) + case NodeTypes.TEXT: + return escapeHtml(node.content) + case NodeTypes.COMMENT: + return `` + case NodeTypes.INTERPOLATION: + return escapeHtml(toDisplayString(evaluateConstant(node.content))) + case NodeTypes.COMPOUND_EXPRESSION: + return escapeHtml(evaluateConstant(node)) + case NodeTypes.TEXT_CALL: + return stringifyNode(node.content, context) + default: + // static trees will not contain if/for nodes + return '' + } +} + function stringifyElement( node: ElementNode, context: TransformContext @@ -235,35 +271,6 @@ function stringifyElement( return res } -function stringifyNode( - node: string | TemplateChildNode, - context: TransformContext -): string { - if (isString(node)) { - return node - } - if (isSymbol(node)) { - return `` - } - switch (node.type) { - case NodeTypes.ELEMENT: - return stringifyElement(node, context) - case NodeTypes.TEXT: - return escapeHtml(node.content) - case NodeTypes.COMMENT: - return `` - case NodeTypes.INTERPOLATION: - return escapeHtml(toDisplayString(evaluateConstant(node.content))) - case NodeTypes.COMPOUND_EXPRESSION: - return escapeHtml(evaluateConstant(node)) - case NodeTypes.TEXT_CALL: - return stringifyNode(node.content, context) - default: - // static trees will not contain if/for nodes - return '' - } -} - // __UNSAFE__ // Reason: eval. // It's technically safe to eval because only constant expressions are possible