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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user