test: tests for hoistStatic

This commit is contained in:
Evan You
2019-10-07 17:12:22 -04:00
parent 802ecccc49
commit 57a5c61320
9 changed files with 655 additions and 83 deletions

View File

@@ -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']>(

View File

@@ -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]

View File

@@ -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)
}
}
}