2019-10-04 11:30:25 +08:00
|
|
|
import {
|
|
|
|
RootNode,
|
|
|
|
NodeTypes,
|
|
|
|
TemplateChildNode,
|
2019-10-05 02:34:26 +08:00
|
|
|
ElementNode,
|
2019-10-06 10:47:20 +08:00
|
|
|
ElementTypes,
|
|
|
|
ElementCodegenNode,
|
|
|
|
ElementCodegenNodeWithDirective
|
2019-10-04 11:30:25 +08:00
|
|
|
} from '../ast'
|
|
|
|
import { TransformContext } from '../transform'
|
2019-10-06 05:18:25 +08:00
|
|
|
import { APPLY_DIRECTIVES } from '../runtimeHelpers'
|
2019-10-05 10:47:09 +08:00
|
|
|
import { PatchFlags } from '@vue/shared'
|
2019-10-04 11:30:25 +08:00
|
|
|
|
2019-10-04 21:03:00 +08:00
|
|
|
export function hoistStatic(root: RootNode, context: TransformContext) {
|
2019-10-05 02:34:26 +08:00
|
|
|
walk(root.children, context, new Map<TemplateChildNode, boolean>())
|
2019-10-04 11:30:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function walk(
|
|
|
|
children: TemplateChildNode[],
|
|
|
|
context: TransformContext,
|
2019-10-05 02:34:26 +08:00
|
|
|
resultCache: Map<TemplateChildNode, boolean>
|
2019-10-04 11:30:25 +08:00
|
|
|
) {
|
|
|
|
for (let i = 0; i < children.length; i++) {
|
|
|
|
const child = children[i]
|
2019-10-05 02:34:26 +08:00
|
|
|
// only plain elements are eligible for hoisting.
|
|
|
|
if (
|
|
|
|
child.type === NodeTypes.ELEMENT &&
|
|
|
|
child.tagType === ElementTypes.ELEMENT
|
|
|
|
) {
|
|
|
|
if (isStaticNode(child, resultCache)) {
|
2019-10-04 11:30:25 +08:00
|
|
|
// whole tree is static
|
2019-10-06 10:47:20 +08:00
|
|
|
;(child as any).codegenNode = context.hoist(child.codegenNode!)
|
2019-10-04 11:30:25 +08:00
|
|
|
continue
|
2019-10-05 02:34:26 +08:00
|
|
|
} else {
|
|
|
|
// node may contain dynamic children, but its props may be eligible for
|
|
|
|
// hoisting.
|
|
|
|
const flag = getPatchFlag(child)
|
2019-10-05 10:51:51 +08:00
|
|
|
if (
|
|
|
|
!flag ||
|
|
|
|
flag === PatchFlags.NEED_PATCH ||
|
|
|
|
flag === PatchFlags.TEXT
|
|
|
|
) {
|
2019-10-06 10:47:20 +08:00
|
|
|
let codegenNode = child.codegenNode!
|
2019-10-06 05:18:25 +08:00
|
|
|
if (codegenNode.callee === APPLY_DIRECTIVES) {
|
2019-10-06 10:47:20 +08:00
|
|
|
codegenNode = codegenNode.arguments[0]
|
2019-10-05 02:34:26 +08:00
|
|
|
}
|
2019-10-06 10:47:20 +08:00
|
|
|
const props = codegenNode.arguments[1]
|
2019-10-05 02:34:26 +08:00
|
|
|
if (props && props !== `null`) {
|
2019-10-06 10:47:20 +08:00
|
|
|
codegenNode.arguments[1] = context.hoist(props)
|
2019-10-05 02:34:26 +08:00
|
|
|
}
|
2019-10-04 11:30:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (child.type === NodeTypes.ELEMENT || child.type === NodeTypes.FOR) {
|
2019-10-05 02:34:26 +08:00
|
|
|
walk(child.children, context, resultCache)
|
2019-10-04 11:30:25 +08:00
|
|
|
} else if (child.type === NodeTypes.IF) {
|
|
|
|
for (let i = 0; i < child.branches.length; i++) {
|
2019-10-05 02:34:26 +08:00
|
|
|
walk(child.branches[i].children, context, resultCache)
|
2019-10-04 11:30:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getPatchFlag(node: ElementNode): number | undefined {
|
2019-10-06 10:47:20 +08:00
|
|
|
let codegenNode = node.codegenNode as
|
|
|
|
| ElementCodegenNode
|
|
|
|
| ElementCodegenNodeWithDirective
|
2019-10-06 05:18:25 +08:00
|
|
|
if (codegenNode.callee === APPLY_DIRECTIVES) {
|
2019-10-06 10:47:20 +08:00
|
|
|
codegenNode = codegenNode.arguments[0]
|
2019-10-04 11:30:25 +08:00
|
|
|
}
|
2019-10-05 02:34:26 +08:00
|
|
|
const flag = codegenNode.arguments[3]
|
|
|
|
return flag ? parseInt(flag as string, 10) : undefined
|
2019-10-04 11:30:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function isStaticNode(
|
|
|
|
node: TemplateChildNode,
|
2019-10-05 02:34:26 +08:00
|
|
|
resultCache: Map<TemplateChildNode, boolean>
|
2019-10-04 11:30:25 +08:00
|
|
|
): boolean {
|
|
|
|
switch (node.type) {
|
|
|
|
case NodeTypes.ELEMENT:
|
2019-10-05 02:34:26 +08:00
|
|
|
if (node.tagType !== ElementTypes.ELEMENT) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if (resultCache.has(node)) {
|
|
|
|
return resultCache.get(node) as boolean
|
2019-10-04 11:30:25 +08:00
|
|
|
}
|
|
|
|
const flag = getPatchFlag(node)
|
|
|
|
if (!flag) {
|
|
|
|
// element self is static. check its children.
|
|
|
|
for (let i = 0; i < node.children.length; i++) {
|
2019-10-05 02:34:26 +08:00
|
|
|
if (!isStaticNode(node.children[i], resultCache)) {
|
|
|
|
resultCache.set(node, false)
|
2019-10-04 11:30:25 +08:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2019-10-05 02:34:26 +08:00
|
|
|
resultCache.set(node, true)
|
2019-10-04 11:30:25 +08:00
|
|
|
return true
|
|
|
|
} else {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
case NodeTypes.TEXT:
|
|
|
|
case NodeTypes.COMMENT:
|
|
|
|
return true
|
|
|
|
case NodeTypes.IF:
|
|
|
|
case NodeTypes.FOR:
|
|
|
|
case NodeTypes.INTERPOLATION:
|
|
|
|
case NodeTypes.COMPOUND_EXPRESSION:
|
|
|
|
return false
|
|
|
|
default:
|
|
|
|
if (__DEV__) {
|
|
|
|
const exhaustiveCheck: never = node
|
|
|
|
exhaustiveCheck
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|