test: tests for hoistStatic
This commit is contained in:
@@ -136,6 +136,10 @@ export interface SlotOutletNode extends BaseElementNode {
|
||||
|
||||
export interface TemplateNode extends BaseElementNode {
|
||||
tagType: ElementTypes.TEMPLATE
|
||||
codegenNode:
|
||||
| ElementCodegenNode
|
||||
| CodegenNodeWithDirective<ElementCodegenNode>
|
||||
| undefined
|
||||
}
|
||||
|
||||
export interface TextNode extends Node {
|
||||
@@ -278,8 +282,7 @@ export interface ConditionalExpression extends Node {
|
||||
// createVNode(...)
|
||||
export interface ElementCodegenNode extends CallExpression {
|
||||
callee: typeof CREATE_VNODE
|
||||
arguments: // tag, props, children, patchFlag, dynamicProps
|
||||
|
||||
arguments: // tag, props, children, patchFlag, dynamicProps
|
||||
| [string | RuntimeHelper]
|
||||
| [string | RuntimeHelper, PropsExpression]
|
||||
| [string | RuntimeHelper, 'null' | PropsExpression, TemplateChildNode[]]
|
||||
@@ -305,8 +308,7 @@ export type ElementCodegenNodeWithDirective = CodegenNodeWithDirective<
|
||||
// createVNode(...)
|
||||
export interface ComponentCodegenNode extends CallExpression {
|
||||
callee: typeof CREATE_VNODE
|
||||
arguments: // Comp, props, slots, patchFlag, dynamicProps
|
||||
|
||||
arguments: // Comp, props, slots, patchFlag, dynamicProps
|
||||
| [string | RuntimeHelper]
|
||||
| [string | RuntimeHelper, PropsExpression]
|
||||
| [string | RuntimeHelper, 'null' | PropsExpression, SlotsExpression]
|
||||
@@ -394,8 +396,7 @@ export interface DirectiveArguments extends ArrayExpression {
|
||||
}
|
||||
|
||||
export interface DirectiveArgumentNode extends ArrayExpression {
|
||||
elements: // dir, exp, arg, modifiers
|
||||
|
||||
elements: // dir, exp, arg, modifiers
|
||||
| [string]
|
||||
| [string, ExpressionNode]
|
||||
| [string, ExpressionNode, ExpressionNode]
|
||||
@@ -405,8 +406,7 @@ export interface DirectiveArgumentNode extends ArrayExpression {
|
||||
// renderSlot(...)
|
||||
export interface SlotOutletCodegenNode extends CallExpression {
|
||||
callee: typeof RENDER_SLOT
|
||||
arguments: // $slots, name, props, fallback
|
||||
|
||||
arguments: // $slots, name, props, fallback
|
||||
| [string, string | ExpressionNode]
|
||||
| [string, string | ExpressionNode, PropsExpression]
|
||||
| [
|
||||
@@ -557,7 +557,7 @@ type InferCodegenNodeType<T> = T extends typeof CREATE_VNODE
|
||||
: T extends typeof CREATE_BLOCK
|
||||
? BlockElementCodegenNode | BlockComponentCodegenNode
|
||||
: T extends typeof APPLY_DIRECTIVES
|
||||
? CodegenNodeWithDirective<ElementCodegenNode | ComponentCodegenNode>
|
||||
? ElementCodegenNodeWithDirective | CompoenntCodegenNodeWithDirective
|
||||
: T extends typeof RENDER_SLOT ? SlotOutletCodegenNode : CallExpression
|
||||
|
||||
export function createCallExpression<T extends CallExpression['callee']>(
|
||||
|
||||
@@ -22,8 +22,8 @@ import {
|
||||
RuntimeHelper,
|
||||
helperNameMap
|
||||
} from './runtimeHelpers'
|
||||
import { isVSlot, createBlockExpression, isSlotOutlet } from './utils'
|
||||
import { hoistStatic } from './transforms/hoistStatic'
|
||||
import { isVSlot, createBlockExpression } from './utils'
|
||||
import { hoistStatic, isSingleElementRoot } from './transforms/hoistStatic'
|
||||
|
||||
// There are two types of transforms:
|
||||
//
|
||||
@@ -229,26 +229,19 @@ export function transform(root: RootNode, options: TransformOptions) {
|
||||
function finalizeRoot(root: RootNode, context: TransformContext) {
|
||||
const { helper } = context
|
||||
const { children } = root
|
||||
if (children.length === 1) {
|
||||
const child = children[0]
|
||||
if (
|
||||
child.type === NodeTypes.ELEMENT &&
|
||||
!isSlotOutlet(child) &&
|
||||
child.codegenNode &&
|
||||
child.codegenNode.type === NodeTypes.JS_CALL_EXPRESSION
|
||||
) {
|
||||
// turn root element into a block
|
||||
root.codegenNode = createBlockExpression(
|
||||
child.codegenNode!.arguments,
|
||||
context
|
||||
)
|
||||
} else {
|
||||
// - single <slot/>, IfNode, ForNode: already blocks.
|
||||
// - single text node: always patched.
|
||||
// - transform calls without transformElement (only during tests)
|
||||
// Just generate the node as-is
|
||||
root.codegenNode = child
|
||||
}
|
||||
const child = children[0]
|
||||
if (isSingleElementRoot(root, child) && child.codegenNode) {
|
||||
// turn root element into a block
|
||||
root.codegenNode = createBlockExpression(
|
||||
child.codegenNode.arguments,
|
||||
context
|
||||
)
|
||||
} else if (children.length === 1) {
|
||||
// - single <slot/>, IfNode, ForNode: already blocks.
|
||||
// - single text node: always patched.
|
||||
// - transform calls without transformElement (only during tests)
|
||||
// Just generate the node as-is
|
||||
root.codegenNode = child
|
||||
} else if (children.length > 1) {
|
||||
// root has multiple nodes - return a fragment block.
|
||||
root.codegenNode = createBlockExpression(
|
||||
@@ -256,7 +249,6 @@ function finalizeRoot(root: RootNode, context: TransformContext) {
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
// finalize meta information
|
||||
root.helpers = [...context.helpers]
|
||||
root.components = [...context.components]
|
||||
|
||||
@@ -5,20 +5,42 @@ import {
|
||||
ElementNode,
|
||||
ElementTypes,
|
||||
ElementCodegenNode,
|
||||
ElementCodegenNodeWithDirective
|
||||
ElementCodegenNodeWithDirective,
|
||||
PlainElementNode,
|
||||
ComponentNode,
|
||||
TemplateNode
|
||||
} from '../ast'
|
||||
import { TransformContext } from '../transform'
|
||||
import { APPLY_DIRECTIVES } from '../runtimeHelpers'
|
||||
import { PatchFlags } from '@vue/shared'
|
||||
import { isSlotOutlet } from '../utils'
|
||||
|
||||
export function hoistStatic(root: RootNode, context: TransformContext) {
|
||||
walk(root.children, context, new Map<TemplateChildNode, boolean>())
|
||||
walk(
|
||||
root.children,
|
||||
context,
|
||||
new Map(),
|
||||
isSingleElementRoot(root, root.children[0])
|
||||
)
|
||||
}
|
||||
|
||||
export function isSingleElementRoot(
|
||||
root: RootNode,
|
||||
child: TemplateChildNode
|
||||
): child is PlainElementNode | ComponentNode | TemplateNode {
|
||||
const { children } = root
|
||||
return (
|
||||
children.length === 1 &&
|
||||
child.type === NodeTypes.ELEMENT &&
|
||||
!isSlotOutlet(child)
|
||||
)
|
||||
}
|
||||
|
||||
function walk(
|
||||
children: TemplateChildNode[],
|
||||
context: TransformContext,
|
||||
resultCache: Map<TemplateChildNode, boolean>
|
||||
resultCache: Map<TemplateChildNode, boolean>,
|
||||
doNotHoistNode: boolean = false
|
||||
) {
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i]
|
||||
@@ -27,7 +49,7 @@ function walk(
|
||||
child.type === NodeTypes.ELEMENT &&
|
||||
child.tagType === ElementTypes.ELEMENT
|
||||
) {
|
||||
if (isStaticNode(child, resultCache)) {
|
||||
if (!doNotHoistNode && isStaticNode(child, resultCache)) {
|
||||
// whole tree is static
|
||||
;(child as any).codegenNode = context.hoist(child.codegenNode!)
|
||||
continue
|
||||
@@ -51,11 +73,16 @@ function walk(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (child.type === NodeTypes.ELEMENT || child.type === NodeTypes.FOR) {
|
||||
if (child.type === NodeTypes.ELEMENT) {
|
||||
walk(child.children, context, resultCache)
|
||||
} else if (child.type === NodeTypes.FOR) {
|
||||
// Do not hoist v-for single child because it has to be a block
|
||||
walk(child.children, context, resultCache, child.children.length === 1)
|
||||
} else if (child.type === NodeTypes.IF) {
|
||||
for (let i = 0; i < child.branches.length; i++) {
|
||||
walk(child.branches[i].children, context, resultCache)
|
||||
const branchChildren = child.branches[i].children
|
||||
// Do not hoist v-if single child because it has to be a block
|
||||
walk(branchChildren, context, resultCache, branchChildren.length === 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user