perf(compiler-core): treat v-for with constant exp as a stable fragment (#1394)
This commit is contained in:
parent
8899a90fc4
commit
8a2cf21b71
@ -102,6 +102,15 @@ return function render(_ctx, _cache) {
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: codegen forNode with constant expression 1`] = `
|
||||
"
|
||||
return function render(_ctx, _cache) {
|
||||
with (_ctx) {
|
||||
return (_openBlock(), _createBlock(_Fragment, null, _renderList(), 64 /* STABLE_FRAGMENT */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: codegen function mode preamble 1`] = `
|
||||
"const _Vue = Vue
|
||||
|
||||
|
@ -32,7 +32,7 @@ import {
|
||||
FRAGMENT,
|
||||
RENDER_LIST
|
||||
} from '../src/runtimeHelpers'
|
||||
import { createElementWithCodegen } from './testUtils'
|
||||
import { createElementWithCodegen, genFlagText } from './testUtils'
|
||||
import { PatchFlags } from '@vue/shared'
|
||||
|
||||
function createRoot(options: Partial<RootNode> = {}): RootNode {
|
||||
@ -283,7 +283,7 @@ describe('compiler: codegen', () => {
|
||||
type: NodeTypes.VNODE_CALL,
|
||||
tag: FRAGMENT,
|
||||
isBlock: true,
|
||||
isForBlock: true,
|
||||
disableTracking: true,
|
||||
props: undefined,
|
||||
children: createCallExpression(RENDER_LIST),
|
||||
patchFlag: '1',
|
||||
@ -298,6 +298,37 @@ describe('compiler: codegen', () => {
|
||||
expect(code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('forNode with constant expression', () => {
|
||||
const { code } = generate(
|
||||
createRoot({
|
||||
codegenNode: {
|
||||
type: NodeTypes.FOR,
|
||||
loc: locStub,
|
||||
source: createSimpleExpression('1 + 2', false, locStub, true),
|
||||
valueAlias: undefined,
|
||||
keyAlias: undefined,
|
||||
objectIndexAlias: undefined,
|
||||
children: [],
|
||||
parseResult: {} as any,
|
||||
codegenNode: {
|
||||
type: NodeTypes.VNODE_CALL,
|
||||
tag: FRAGMENT,
|
||||
isBlock: true,
|
||||
disableTracking: false,
|
||||
props: undefined,
|
||||
children: createCallExpression(RENDER_LIST),
|
||||
patchFlag: genFlagText(PatchFlags.STABLE_FRAGMENT),
|
||||
dynamicProps: undefined,
|
||||
directives: undefined,
|
||||
loc: locStub
|
||||
} as ForCodegenNode
|
||||
}
|
||||
})
|
||||
)
|
||||
expect(code).toMatch(`openBlock()`)
|
||||
expect(code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('Element (callExpression + objectExpression + TemplateChildNode[])', () => {
|
||||
const { code } = generate(
|
||||
createRoot({
|
||||
|
@ -62,7 +62,7 @@ export function createElementWithCodegen(
|
||||
dynamicProps,
|
||||
directives: undefined,
|
||||
isBlock: false,
|
||||
isForBlock: false,
|
||||
disableTracking: false,
|
||||
loc: locStub
|
||||
}
|
||||
}
|
||||
|
@ -150,6 +150,20 @@ return function render(_ctx, _cache) {
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: v-for codegen v-for with constant expression 1`] = `
|
||||
"const _Vue = Vue
|
||||
|
||||
return function render(_ctx, _cache) {
|
||||
with (_ctx) {
|
||||
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, toDisplayString: _toDisplayString, createVNode: _createVNode } = _Vue
|
||||
|
||||
return (_openBlock(), _createBlock(_Fragment, null, _renderList(10, (item) => {
|
||||
return _createVNode(\\"p\\", null, _toDisplayString(item), 1 /* TEXT */)
|
||||
}), 64 /* STABLE_FRAGMENT */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: v-for codegen v-if + v-for 1`] = `
|
||||
"const _Vue = Vue
|
||||
|
||||
|
@ -560,15 +560,18 @@ describe('compiler: v-for', () => {
|
||||
function assertSharedCodegen(
|
||||
node: ForCodegenNode,
|
||||
keyed: boolean = false,
|
||||
customReturn: boolean = false
|
||||
customReturn: boolean = false,
|
||||
disableTracking: boolean = true
|
||||
) {
|
||||
expect(node).toMatchObject({
|
||||
type: NodeTypes.VNODE_CALL,
|
||||
tag: FRAGMENT,
|
||||
isForBlock: true,
|
||||
patchFlag: keyed
|
||||
? genFlagText(PatchFlags.KEYED_FRAGMENT)
|
||||
: genFlagText(PatchFlags.UNKEYED_FRAGMENT),
|
||||
disableTracking,
|
||||
patchFlag: !disableTracking
|
||||
? genFlagText(PatchFlags.STABLE_FRAGMENT)
|
||||
: keyed
|
||||
? genFlagText(PatchFlags.KEYED_FRAGMENT)
|
||||
: genFlagText(PatchFlags.UNKEYED_FRAGMENT),
|
||||
children: {
|
||||
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||
callee: RENDER_LIST,
|
||||
@ -580,7 +583,7 @@ describe('compiler: v-for', () => {
|
||||
? {}
|
||||
: {
|
||||
type: NodeTypes.VNODE_CALL,
|
||||
isBlock: true
|
||||
isBlock: disableTracking
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -658,6 +661,43 @@ describe('compiler: v-for', () => {
|
||||
expect(generate(root).code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('v-for with constant expression', () => {
|
||||
const {
|
||||
root,
|
||||
node: { codegenNode }
|
||||
} = parseWithForTransform('<p v-for="item in 10">{{item}}</p>', {
|
||||
prefixIdentifiers: true
|
||||
})
|
||||
|
||||
expect(
|
||||
assertSharedCodegen(
|
||||
codegenNode,
|
||||
false /* keyed */,
|
||||
false /* customReturn */,
|
||||
false /* disableTracking */
|
||||
)
|
||||
).toMatchObject({
|
||||
source: { content: `10`, isConstant: true },
|
||||
params: [{ content: `item` }],
|
||||
innerVNodeCall: {
|
||||
tag: `"p"`,
|
||||
props: undefined,
|
||||
isBlock: false,
|
||||
children: {
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'item',
|
||||
isStatic: false,
|
||||
isConstant: false
|
||||
}
|
||||
},
|
||||
patchFlag: genFlagText(PatchFlags.TEXT)
|
||||
}
|
||||
})
|
||||
expect(generate(root).code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('template v-for', () => {
|
||||
const {
|
||||
root,
|
||||
@ -777,7 +817,7 @@ describe('compiler: v-for', () => {
|
||||
key: `[0]`
|
||||
}),
|
||||
isBlock: true,
|
||||
isForBlock: true,
|
||||
disableTracking: true,
|
||||
patchFlag: genFlagText(PatchFlags.UNKEYED_FRAGMENT),
|
||||
children: {
|
||||
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||
|
@ -281,7 +281,7 @@ export interface VNodeCall extends Node {
|
||||
dynamicProps: string | undefined
|
||||
directives: DirectiveArguments | undefined
|
||||
isBlock: boolean
|
||||
isForBlock: boolean
|
||||
disableTracking: boolean
|
||||
}
|
||||
|
||||
// JS Node Types ---------------------------------------------------------------
|
||||
@ -492,7 +492,7 @@ export interface ForCodegenNode extends VNodeCall {
|
||||
props: undefined
|
||||
children: ForRenderListExpression
|
||||
patchFlag: string
|
||||
isForBlock: true
|
||||
disableTracking: boolean
|
||||
}
|
||||
|
||||
export interface ForRenderListExpression extends CallExpression {
|
||||
@ -543,7 +543,7 @@ export function createVNodeCall(
|
||||
dynamicProps?: VNodeCall['dynamicProps'],
|
||||
directives?: VNodeCall['directives'],
|
||||
isBlock: VNodeCall['isBlock'] = false,
|
||||
isForBlock: VNodeCall['isForBlock'] = false,
|
||||
disableTracking: VNodeCall['disableTracking'] = false,
|
||||
loc = locStub
|
||||
): VNodeCall {
|
||||
if (context) {
|
||||
@ -567,7 +567,7 @@ export function createVNodeCall(
|
||||
dynamicProps,
|
||||
directives,
|
||||
isBlock,
|
||||
isForBlock,
|
||||
disableTracking,
|
||||
loc
|
||||
}
|
||||
}
|
||||
|
@ -698,13 +698,13 @@ function genVNodeCall(node: VNodeCall, context: CodegenContext) {
|
||||
dynamicProps,
|
||||
directives,
|
||||
isBlock,
|
||||
isForBlock
|
||||
disableTracking
|
||||
} = node
|
||||
if (directives) {
|
||||
push(helper(WITH_DIRECTIVES) + `(`)
|
||||
}
|
||||
if (isBlock) {
|
||||
push(`(${helper(OPEN_BLOCK)}(${isForBlock ? `true` : ``}), `)
|
||||
push(`(${helper(OPEN_BLOCK)}(${disableTracking ? `true` : ``}), `)
|
||||
}
|
||||
if (pure) {
|
||||
push(PURE_ANNOTATION)
|
||||
|
@ -203,7 +203,7 @@ export const transformElement: NodeTransform = (node, context) => {
|
||||
vnodeDynamicProps,
|
||||
vnodeDirectives,
|
||||
!!shouldUseBlock,
|
||||
false /* isForBlock */,
|
||||
false /* disableTracking */,
|
||||
node.loc
|
||||
)
|
||||
}
|
||||
|
@ -55,9 +55,14 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
forNode.source
|
||||
]) as ForRenderListExpression
|
||||
const keyProp = findProp(node, `key`)
|
||||
const fragmentFlag = keyProp
|
||||
? PatchFlags.KEYED_FRAGMENT
|
||||
: PatchFlags.UNKEYED_FRAGMENT
|
||||
const isStableFragment =
|
||||
forNode.source.type === NodeTypes.SIMPLE_EXPRESSION &&
|
||||
forNode.source.isConstant
|
||||
const fragmentFlag = isStableFragment
|
||||
? PatchFlags.STABLE_FRAGMENT
|
||||
: keyProp
|
||||
? PatchFlags.KEYED_FRAGMENT
|
||||
: PatchFlags.UNKEYED_FRAGMENT
|
||||
forNode.codegenNode = createVNodeCall(
|
||||
context,
|
||||
helper(FRAGMENT),
|
||||
@ -67,7 +72,7 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
undefined,
|
||||
undefined,
|
||||
true /* isBlock */,
|
||||
true /* isForBlock */,
|
||||
!isStableFragment /* disableTracking */,
|
||||
node.loc
|
||||
) as ForCodegenNode
|
||||
|
||||
@ -122,9 +127,11 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
// but mark it as a block.
|
||||
childBlock = (children[0] as PlainElementNode)
|
||||
.codegenNode as VNodeCall
|
||||
childBlock.isBlock = true
|
||||
helper(OPEN_BLOCK)
|
||||
helper(CREATE_BLOCK)
|
||||
childBlock.isBlock = !isStableFragment
|
||||
if (childBlock.isBlock) {
|
||||
helper(OPEN_BLOCK)
|
||||
helper(CREATE_BLOCK)
|
||||
}
|
||||
}
|
||||
|
||||
renderExp.arguments.push(createFunctionExpression(
|
||||
|
Loading…
Reference in New Issue
Block a user