wip(compiler-ssr): v-if

This commit is contained in:
Evan You
2020-02-03 15:51:41 -05:00
parent 090eb0ce67
commit e8c5de6cfd
14 changed files with 351 additions and 137 deletions

View File

@@ -23,7 +23,8 @@ import {
BlockCodegenNode,
SlotOutletCodegenNode,
ElementCodegenNode,
ComponentCodegenNode
ComponentCodegenNode,
IfNode
} from '../ast'
import { createCompilerError, ErrorCodes } from '../errors'
import { processExpression } from './transformExpression'
@@ -40,73 +41,18 @@ import { injectProp } from '../utils'
export const transformIf = createStructuralDirectiveTransform(
/^(if|else|else-if)$/,
(node, dir, context) => {
if (
dir.name !== 'else' &&
(!dir.exp || !(dir.exp as SimpleExpressionNode).content.trim())
) {
const loc = dir.exp ? dir.exp.loc : node.loc
context.onError(
createCompilerError(ErrorCodes.X_V_IF_NO_EXPRESSION, dir.loc)
)
dir.exp = createSimpleExpression(`true`, false, loc)
}
if (!__BROWSER__ && context.prefixIdentifiers && dir.exp) {
// dir.exp can only be simple expression because vIf transform is applied
// before expression transform.
dir.exp = processExpression(dir.exp as SimpleExpressionNode, context)
}
if (dir.name === 'if') {
const branch = createIfBranch(node, dir)
const codegenNode = createSequenceExpression([
createCallExpression(context.helper(OPEN_BLOCK))
]) as IfCodegenNode
context.replaceNode({
type: NodeTypes.IF,
loc: node.loc,
branches: [branch],
codegenNode
})
return processIfBranches(node, dir, context, (ifNode, branch, isRoot) => {
// Exit callback. Complete the codegenNode when all children have been
// transformed.
return () => {
codegenNode.expressions.push(createCodegenNodeForBranch(
branch,
0,
context
) as IfConditionalExpression)
}
} else {
// locate the adjacent v-if
const siblings = context.parent!.children
const comments = []
let i = siblings.indexOf(node)
while (i-- >= -1) {
const sibling = siblings[i]
if (__DEV__ && sibling && sibling.type === NodeTypes.COMMENT) {
context.removeNode(sibling)
comments.unshift(sibling)
continue
}
if (sibling && sibling.type === NodeTypes.IF) {
// move the node to the if node's branches
context.removeNode()
const branch = createIfBranch(node, dir)
if (__DEV__ && comments.length) {
branch.children = [...comments, ...branch.children]
}
sibling.branches.push(branch)
// since the branch was removed, it will not be traversed.
// make sure to traverse here.
traverseChildren(branch, context)
// make sure to reset currentNode after traversal to indicate this
// node has been removed.
context.currentNode = null
if (isRoot) {
ifNode.codegenNode = createSequenceExpression([
createCallExpression(context.helper(OPEN_BLOCK)),
createCodegenNodeForBranch(branch, 0, context)
]) as IfCodegenNode
} else {
// attach this branch's codegen node to the v-if root.
let parentCondition = sibling.codegenNode
let parentCondition = ifNode.codegenNode!
.expressions[1] as ConditionalExpression
while (
parentCondition.alternate.type ===
@@ -116,20 +62,92 @@ export const transformIf = createStructuralDirectiveTransform(
}
parentCondition.alternate = createCodegenNodeForBranch(
branch,
sibling.branches.length - 1,
ifNode.branches.length - 1,
context
)
} else {
context.onError(
createCompilerError(ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, node.loc)
)
}
break
}
}
})
}
)
export const processIfBranches = (
node: ElementNode,
dir: DirectiveNode,
context: TransformContext,
processCodegen?: (
node: IfNode,
branch: IfBranchNode,
isRoot: boolean
) => (() => void) | void
) => {
if (
dir.name !== 'else' &&
(!dir.exp || !(dir.exp as SimpleExpressionNode).content.trim())
) {
const loc = dir.exp ? dir.exp.loc : node.loc
context.onError(
createCompilerError(ErrorCodes.X_V_IF_NO_EXPRESSION, dir.loc)
)
dir.exp = createSimpleExpression(`true`, false, loc)
}
if (!__BROWSER__ && context.prefixIdentifiers && dir.exp) {
// dir.exp can only be simple expression because vIf transform is applied
// before expression transform.
dir.exp = processExpression(dir.exp as SimpleExpressionNode, context)
}
if (dir.name === 'if') {
const branch = createIfBranch(node, dir)
const ifNode: IfNode = {
type: NodeTypes.IF,
loc: node.loc,
branches: [branch]
}
context.replaceNode(ifNode)
if (processCodegen) {
return processCodegen(ifNode, branch, true)
}
} else {
// locate the adjacent v-if
const siblings = context.parent!.children
const comments = []
let i = siblings.indexOf(node)
while (i-- >= -1) {
const sibling = siblings[i]
if (__DEV__ && sibling && sibling.type === NodeTypes.COMMENT) {
context.removeNode(sibling)
comments.unshift(sibling)
continue
}
if (sibling && sibling.type === NodeTypes.IF) {
// move the node to the if node's branches
context.removeNode()
const branch = createIfBranch(node, dir)
if (__DEV__ && comments.length) {
branch.children = [...comments, ...branch.children]
}
sibling.branches.push(branch)
const onExit = processCodegen && processCodegen(sibling, branch, false)
// since the branch was removed, it will not be traversed.
// make sure to traverse here.
traverseChildren(branch, context)
// call on exit
if (onExit) onExit()
// make sure to reset currentNode after traversal to indicate this
// node has been removed.
context.currentNode = null
} else {
context.onError(
createCompilerError(ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, node.loc)
)
}
break
}
}
}
function createIfBranch(node: ElementNode, dir: DirectiveNode): IfBranchNode {
return {
type: NodeTypes.IF_BRANCH,
@@ -171,25 +189,25 @@ function createChildrenCodegenNode(
createSimpleExpression(index + '', false)
)
const { children } = branch
const child = children[0]
const firstChild = children[0]
const needFragmentWrapper =
children.length !== 1 || child.type !== NodeTypes.ELEMENT
children.length !== 1 || firstChild.type !== NodeTypes.ELEMENT
if (needFragmentWrapper) {
const blockArgs: CallExpression['arguments'] = [
helper(FRAGMENT),
createObjectExpression([keyProperty]),
children
]
if (children.length === 1 && child.type === NodeTypes.FOR) {
if (children.length === 1 && firstChild.type === NodeTypes.FOR) {
// optimize away nested fragments when child is a ForNode
const forBlockArgs = child.codegenNode.expressions[1].arguments
const forBlockArgs = firstChild.codegenNode!.expressions[1].arguments
// directly use the for block's children and patchFlag
blockArgs[2] = forBlockArgs[2]
blockArgs[3] = forBlockArgs[3]
}
return createCallExpression(helper(CREATE_BLOCK), blockArgs)
} else {
const childCodegen = (child as ElementNode).codegenNode as
const childCodegen = (firstChild as ElementNode).codegenNode as
| ElementCodegenNode
| ComponentCodegenNode
| SlotOutletCodegenNode