wip(compiler): codegen node w/ block optimization for v-for

This commit is contained in:
Evan You
2019-10-01 16:48:20 -04:00
parent aa134e7a4f
commit e5bc17967d
9 changed files with 353 additions and 409 deletions

View File

@@ -7,11 +7,20 @@ import {
ExpressionNode,
createSimpleExpression,
SourceLocation,
SimpleExpressionNode
SimpleExpressionNode,
createSequenceExpression,
createCallExpression,
createFunctionExpression,
ElementTypes
} from '../ast'
import { createCompilerError, ErrorCodes } from '../errors'
import { getInnerRange } from '../utils'
import { RENDER_LIST } from '../runtimeConstants'
import {
RENDER_LIST,
OPEN_BLOCK,
CREATE_BLOCK,
FRAGMENT
} from '../runtimeConstants'
import { processExpression } from './transformExpression'
export const transformFor = createStructuralDirectiveTransform(
@@ -26,9 +35,14 @@ export const transformFor = createStructuralDirectiveTransform(
)
if (parseResult) {
context.helper(RENDER_LIST)
const { helper, addIdentifiers, removeIdentifiers } = context
const { source, value, key, index } = parseResult
const codegenNode = createSequenceExpression([
createCallExpression(helper(OPEN_BLOCK))
// to be filled in on exit after children traverse
])
context.replaceNode({
type: NodeTypes.FOR,
loc: dir.loc,
@@ -36,19 +50,52 @@ export const transformFor = createStructuralDirectiveTransform(
valueAlias: value,
keyAlias: key,
objectIndexAlias: index,
children: [node]
children: [node],
codegenNode
})
if (!__BROWSER__) {
// scope management
const { addIdentifiers, removeIdentifiers } = context
// inject identifiers to context
value && addIdentifiers(value)
key && addIdentifiers(key)
index && addIdentifiers(index)
}
return () => {
return () => {
const params: ExpressionNode[] = []
if (value) {
params.push(value)
}
if (key) {
if (!value) {
params.push(createSimpleExpression(`_`, false))
}
params.push(key)
}
if (index) {
if (!key) {
params.push(createSimpleExpression(`__`, false))
}
params.push(index)
}
codegenNode.expressions.push(
createCallExpression(helper(CREATE_BLOCK), [
helper(FRAGMENT),
`null`,
createCallExpression(helper(RENDER_LIST), [
source,
createFunctionExpression(
params,
node.tagType === ElementTypes.TEMPLATE ? node.children : node,
true /* force newline to make it more readable */
)
])
])
)
if (!__BROWSER__) {
value && removeIdentifiers(value)
key && removeIdentifiers(key)
index && removeIdentifiers(index)

View File

@@ -20,7 +20,9 @@ import {
ObjectExpression,
createObjectProperty,
Property,
ExpressionNode
ExpressionNode,
TemplateChildNode,
FunctionExpression
} from '../ast'
import { createCompilerError, ErrorCodes } from '../errors'
import { processExpression } from './transformExpression'
@@ -68,7 +70,7 @@ export const transformIf = createStructuralDirectiveTransform(
// transformed.
return () => {
codegenNode.expressions.push(
createCodegenNodeForBranch(node, branch, 0, context)
createCodegenNodeForBranch(branch, 0, context)
)
}
} else {
@@ -105,7 +107,6 @@ export const transformIf = createStructuralDirectiveTransform(
parentCondition = parentCondition.alternate
} else {
parentCondition.alternate = createCodegenNodeForBranch(
node,
branch,
sibling.branches.length - 1,
context
@@ -139,7 +140,6 @@ function createIfBranch(node: ElementNode, dir: DirectiveNode): IfBranchNode {
}
function createCodegenNodeForBranch(
node: ElementNode,
branch: IfBranchNode,
index: number,
context: TransformContext
@@ -147,41 +147,50 @@ function createCodegenNodeForBranch(
if (branch.condition) {
return createConditionalExpression(
branch.condition,
createChildrenCodegenNode(node, branch, index, context),
createChildrenCodegenNode(branch, index, context),
createCallExpression(context.helper(CREATE_BLOCK), [
context.helper(EMPTY)
])
)
} else {
return createChildrenCodegenNode(node, branch, index, context)
return createChildrenCodegenNode(branch, index, context)
}
}
function createChildrenCodegenNode(
node: ElementNode,
branch: IfBranchNode,
index: number,
{ helper }: TransformContext
): CallExpression {
const isTemplate = node.tagType === ElementTypes.TEMPLATE
const keyExp = `{ key: ${index} }`
if (isTemplate) {
const { children } = branch
const child = children[0]
const needFragmentWrapper =
children.length > 1 || child.type !== NodeTypes.ELEMENT
if (needFragmentWrapper) {
let fragmentChildren: TemplateChildNode[] | FunctionExpression = children
// optimize away nested fragments when child is a ForNode
if (children.length === 1 && child.type === NodeTypes.FOR) {
fragmentChildren = (child.codegenNode.expressions[1] as CallExpression)
.arguments[2] as FunctionExpression
}
return createCallExpression(helper(CREATE_BLOCK), [
helper(FRAGMENT),
keyExp,
branch.children
fragmentChildren
])
} else {
let childCodegen = node.codegenNode!
if (childCodegen.callee.includes(APPLY_DIRECTIVES)) {
childCodegen = childCodegen.arguments[0] as CallExpression
const childCodegen = (child as ElementNode).codegenNode!
let vnodeCall = childCodegen
if (vnodeCall.callee.includes(APPLY_DIRECTIVES)) {
vnodeCall = vnodeCall.arguments[0] as CallExpression
}
// change child to a block
childCodegen.callee = helper(CREATE_BLOCK)
vnodeCall.callee = helper(CREATE_BLOCK)
// branch key
const existingProps = childCodegen.arguments[1]
const existingProps = vnodeCall.arguments[1]
if (!existingProps || existingProps === `null`) {
childCodegen.arguments[1] = keyExp
vnodeCall.arguments[1] = keyExp
} else {
// inject branch key if not already have a key
const props = existingProps as
@@ -202,13 +211,13 @@ function createChildrenCodegenNode(
props.properties.unshift(createKeyProperty(index))
} else {
// single v-bind with expression
childCodegen.arguments[1] = createCallExpression(helper(MERGE_PROPS), [
vnodeCall.arguments[1] = createCallExpression(helper(MERGE_PROPS), [
keyExp,
props
])
}
}
return node.codegenNode!
return childCodegen
}
}

View File

@@ -143,6 +143,7 @@ function buildSlot(
createFunctionExpression(
slotProps,
children,
false,
children.length ? children[0].loc : loc
)
)