feat(compiler): v-for codegen w/ correct blocks optimization + key flags
This commit is contained in:
@@ -163,7 +163,7 @@ export function buildProps(
|
||||
// patchFlag analysis
|
||||
let patchFlag = 0
|
||||
const dynamicPropNames: string[] = []
|
||||
let hasDynammicKeys = false
|
||||
let hasDynamicKeys = false
|
||||
let hasClassBinding = false
|
||||
let hasStyleBinding = false
|
||||
let hasRef = false
|
||||
@@ -207,7 +207,7 @@ export function buildProps(
|
||||
// special case for v-bind and v-on with no argument
|
||||
const isBind = name === 'bind'
|
||||
if (!arg && (isBind || name === 'on')) {
|
||||
hasDynammicKeys = true
|
||||
hasDynamicKeys = true
|
||||
if (exp) {
|
||||
if (properties.length) {
|
||||
mergeArgs.push(
|
||||
@@ -249,11 +249,11 @@ export function buildProps(
|
||||
hasClassBinding = true
|
||||
} else if (name === 'style') {
|
||||
hasStyleBinding = true
|
||||
} else {
|
||||
} else if (name !== 'key') {
|
||||
dynamicPropNames.push(name)
|
||||
}
|
||||
} else {
|
||||
hasDynammicKeys = true
|
||||
hasDynamicKeys = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,7 +303,7 @@ export function buildProps(
|
||||
}
|
||||
|
||||
// determine the flags to add
|
||||
if (hasDynammicKeys) {
|
||||
if (hasDynamicKeys) {
|
||||
patchFlag |= PatchFlags.FULL_PROPS
|
||||
} else {
|
||||
if (hasClassBinding) {
|
||||
|
||||
@@ -11,17 +11,22 @@ import {
|
||||
createSequenceExpression,
|
||||
createCallExpression,
|
||||
createFunctionExpression,
|
||||
ElementTypes
|
||||
ElementTypes,
|
||||
ObjectExpression,
|
||||
createObjectExpression,
|
||||
createObjectProperty
|
||||
} from '../ast'
|
||||
import { createCompilerError, ErrorCodes } from '../errors'
|
||||
import { getInnerRange } from '../utils'
|
||||
import { getInnerRange, findProp } from '../utils'
|
||||
import {
|
||||
RENDER_LIST,
|
||||
OPEN_BLOCK,
|
||||
CREATE_BLOCK,
|
||||
FRAGMENT
|
||||
FRAGMENT,
|
||||
CREATE_VNODE
|
||||
} from '../runtimeConstants'
|
||||
import { processExpression } from './transformExpression'
|
||||
import { PatchFlags, PatchFlagNames } from '@vue/shared'
|
||||
|
||||
export const transformFor = createStructuralDirectiveTransform(
|
||||
'for',
|
||||
@@ -38,9 +43,19 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
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
|
||||
// create the loop render function expression now, and add the
|
||||
// iterator on exit after all children have been traversed
|
||||
const renderExp = createCallExpression(helper(RENDER_LIST), [source])
|
||||
const keyProp = findProp(node.props, `key`)
|
||||
const fragmentFlag = keyProp
|
||||
? PatchFlags.KEYED_FRAGMENT
|
||||
: PatchFlags.UNKEYED_FRAGMENT
|
||||
const codegenNode = createCallExpression(helper(CREATE_VNODE), [
|
||||
helper(FRAGMENT),
|
||||
`null`,
|
||||
renderExp,
|
||||
fragmentFlag +
|
||||
(__DEV__ ? ` /* ${PatchFlagNames[fragmentFlag]} */` : ``)
|
||||
])
|
||||
|
||||
context.replaceNode({
|
||||
@@ -63,6 +78,13 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (!__BROWSER__ && context.prefixIdentifiers) {
|
||||
value && removeIdentifiers(value)
|
||||
key && removeIdentifiers(key)
|
||||
index && removeIdentifiers(index)
|
||||
}
|
||||
|
||||
// finish the codegen now that all children have been traversed
|
||||
const params: ExpressionNode[] = []
|
||||
if (value) {
|
||||
params.push(value)
|
||||
@@ -83,26 +105,46 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
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 */
|
||||
let childBlock
|
||||
if (node.tagType === ElementTypes.TEMPLATE) {
|
||||
// <template v-for="...">
|
||||
// should genereate a fragment block for each loop
|
||||
let childBlockProps: string | ObjectExpression = `null`
|
||||
if (keyProp) {
|
||||
childBlockProps = createObjectExpression([
|
||||
createObjectProperty(
|
||||
createSimpleExpression(`key`, true),
|
||||
keyProp.type === NodeTypes.ATTRIBUTE
|
||||
? createSimpleExpression(keyProp.value!.content, true)
|
||||
: keyProp.exp!
|
||||
)
|
||||
])
|
||||
}
|
||||
childBlock = createSequenceExpression([
|
||||
createCallExpression(helper(OPEN_BLOCK)),
|
||||
createCallExpression(helper(CREATE_BLOCK), [
|
||||
helper(FRAGMENT),
|
||||
childBlockProps,
|
||||
node.children
|
||||
])
|
||||
])
|
||||
} else {
|
||||
// Normal element v-for. Directly use the child's codegenNode,
|
||||
// but replace createVNode() with createBlock()
|
||||
node.codegenNode!.callee = helper(CREATE_BLOCK)
|
||||
childBlock = createSequenceExpression([
|
||||
createCallExpression(helper(OPEN_BLOCK)),
|
||||
node.codegenNode!
|
||||
])
|
||||
)
|
||||
|
||||
if (!__BROWSER__ && context.prefixIdentifiers) {
|
||||
value && removeIdentifiers(value)
|
||||
key && removeIdentifiers(key)
|
||||
index && removeIdentifiers(index)
|
||||
}
|
||||
|
||||
renderExp.arguments.push(
|
||||
createFunctionExpression(
|
||||
params,
|
||||
childBlock,
|
||||
true /* force newline */
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
context.onError(
|
||||
|
||||
@@ -20,9 +20,7 @@ import {
|
||||
ObjectExpression,
|
||||
createObjectProperty,
|
||||
Property,
|
||||
ExpressionNode,
|
||||
TemplateChildNode,
|
||||
FunctionExpression
|
||||
ExpressionNode
|
||||
} from '../ast'
|
||||
import { createCompilerError, ErrorCodes } from '../errors'
|
||||
import { processExpression } from './transformExpression'
|
||||
@@ -168,17 +166,19 @@ function createChildrenCodegenNode(
|
||||
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), [
|
||||
const blockArgs: CallExpression['arguments'] = [
|
||||
helper(FRAGMENT),
|
||||
keyExp,
|
||||
fragmentChildren
|
||||
])
|
||||
children
|
||||
]
|
||||
// optimize away nested fragments when child is a ForNode
|
||||
if (children.length === 1 && child.type === NodeTypes.FOR) {
|
||||
const forBlockExp = child.codegenNode
|
||||
// directly use the for block's children and patchFlag
|
||||
blockArgs[2] = forBlockExp.arguments[2]
|
||||
blockArgs[3] = forBlockExp.arguments[3]
|
||||
}
|
||||
return createCallExpression(helper(CREATE_BLOCK), blockArgs)
|
||||
} else {
|
||||
const childCodegen = (child as ElementNode).codegenNode!
|
||||
let vnodeCall = childCodegen
|
||||
|
||||
Reference in New Issue
Block a user