class: _ctx.bar.baz
}, [
toString(_ctx.world.burn()),
- (openBlock(), (_ctx.ok)
+ (openBlock(), _ctx.ok)
? createBlock(\\"div\\", { key: 0 }, \\"yes\\")
: createBlock(Fragment, { key: 1 }, [\\"no\\"])),
(openBlock(), createBlock(Fragment, null, renderList(_ctx.list, (value, index) => {
class: _ctx.bar.baz
}, [
_toString(_ctx.world.burn()),
- (openBlock(), (_ctx.ok)
+ (openBlock(), _ctx.ok)
? createBlock(\\"div\\", { key: 0 }, \\"yes\\")
: createBlock(Fragment, { key: 1 }, [\\"no\\"])),
(openBlock(), createBlock(Fragment, null, renderList(_ctx.list, (value, index) => {
}
}
genHoists(ast.hoists, context)
+ context.newline()
push(`return `)
} else {
// generate import statements for helpers
push(`import { ${ast.imports.join(', ')} } from "vue"\n`)
}
genHoists(ast.hoists, context)
+ context.newline()
push(`export default `)
}
}
function genHoists(hoists: JSChildNode[], context: CodegenContext) {
+ if (!hoists.length) {
+ return
+ }
+ context.newline()
hoists.forEach((exp, i) => {
context.push(`const _hoisted_${i + 1} = `)
genNode(exp, context)
context.newline()
})
- context.newline()
}
function isText(n: string | CodegenNode) {
}
}
-function genNode(node: CodegenNode, context: CodegenContext) {
+function genNode(node: CodegenNode | string, context: CodegenContext) {
+ if (isString(node)) {
+ context.push(node)
+ return
+ }
switch (node.type) {
case NodeTypes.ELEMENT:
case NodeTypes.IF:
const { test, consequent, alternate } = node
const { push, indent, deindent, newline } = context
if (test.type === NodeTypes.SIMPLE_EXPRESSION) {
- const needsQuote = !isSimpleIdentifier(test.content)
- needsQuote && push(`(`)
+ const needsParens = !isSimpleIdentifier(test.content)
genExpression(test, context)
- needsQuote && push(`)`)
+ needsParens && push(`)`)
} else {
+ push(`(`)
genCompoundExpression(test, context)
+ push(`)`)
}
indent()
context.indentLevel++
NodeTypes,
TemplateChildNode,
CallExpression,
- ElementNode
+ ElementNode,
+ ElementTypes
} from '../ast'
import { TransformContext } from '../transform'
-import { CREATE_VNODE } from '../runtimeConstants'
+import { APPLY_DIRECTIVES } from '../runtimeConstants'
import { PropsExpression } from './transformElement'
+import { PatchFlags } from '@vue/runtime-dom'
export function hoistStatic(root: RootNode, context: TransformContext) {
- walk(root.children, context, new Set<TemplateChildNode>())
+ walk(root.children, context, new Map<TemplateChildNode, boolean>())
}
function walk(
children: TemplateChildNode[],
context: TransformContext,
- knownStaticNodes: Set<TemplateChildNode>
+ resultCache: Map<TemplateChildNode, boolean>
) {
for (let i = 0; i < children.length; i++) {
const child = children[i]
- if (child.type === NodeTypes.ELEMENT) {
- if (isStaticNode(child, knownStaticNodes)) {
+ // only plain elements are eligible for hoisting.
+ if (
+ child.type === NodeTypes.ELEMENT &&
+ child.tagType === ElementTypes.ELEMENT
+ ) {
+ if (isStaticNode(child, resultCache)) {
// whole tree is static
child.codegenNode = context.hoist(child.codegenNode!)
continue
- } else if (!getPatchFlag(child)) {
- // has dynamic children, but self props are static, hoist props instead
- const props = (child.codegenNode as CallExpression).arguments[1] as
- | PropsExpression
- | `null`
- if (props !== `null`) {
- ;(child.codegenNode as CallExpression).arguments[1] = context.hoist(
- props
- )
+ } else {
+ // node may contain dynamic children, but its props may be eligible for
+ // hoisting.
+ const flag = getPatchFlag(child)
+ if (!flag || flag === PatchFlags.NEED_PATCH) {
+ let codegenNode = child.codegenNode as CallExpression
+ if (codegenNode.callee.includes(APPLY_DIRECTIVES)) {
+ codegenNode = codegenNode.arguments[0] as CallExpression
+ }
+ const props = codegenNode.arguments[1] as
+ | PropsExpression
+ | `null`
+ | undefined
+ if (props && props !== `null`) {
+ ;(child.codegenNode as CallExpression).arguments[1] = context.hoist(
+ props
+ )
+ }
}
}
}
if (child.type === NodeTypes.ELEMENT || child.type === NodeTypes.FOR) {
- walk(child.children, context, knownStaticNodes)
+ walk(child.children, context, resultCache)
} else if (child.type === NodeTypes.IF) {
for (let i = 0; i < child.branches.length; i++) {
- walk(child.branches[i].children, context, knownStaticNodes)
+ walk(child.branches[i].children, context, resultCache)
}
}
}
}
function getPatchFlag(node: ElementNode): number | undefined {
- const codegenNode = node.codegenNode as CallExpression
- if (
- // callee is createVNode (i.e. no runtime directives)
- codegenNode.callee.includes(CREATE_VNODE)
- ) {
- const flag = codegenNode.arguments[3]
- return flag ? parseInt(flag as string, 10) : undefined
+ let codegenNode = node.codegenNode as CallExpression
+ if (codegenNode.callee.includes(APPLY_DIRECTIVES)) {
+ codegenNode = codegenNode.arguments[0] as CallExpression
}
+ const flag = codegenNode.arguments[3]
+ return flag ? parseInt(flag as string, 10) : undefined
}
function isStaticNode(
node: TemplateChildNode,
- knownStaticNodes: Set<TemplateChildNode>
+ resultCache: Map<TemplateChildNode, boolean>
): boolean {
switch (node.type) {
case NodeTypes.ELEMENT:
- if (knownStaticNodes.has(node)) {
- return true
+ if (node.tagType !== ElementTypes.ELEMENT) {
+ return false
+ }
+ if (resultCache.has(node)) {
+ return resultCache.get(node) as boolean
}
const flag = getPatchFlag(node)
if (!flag) {
// element self is static. check its children.
for (let i = 0; i < node.children.length; i++) {
- if (!isStaticNode(node.children[i], knownStaticNodes)) {
+ if (!isStaticNode(node.children[i], resultCache)) {
+ resultCache.set(node, false)
return false
}
}
- knownStaticNodes.add(node)
+ resultCache.set(node, true)
return true
} else {
return false