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`] = ` | exports[`compiler: codegen function mode preamble 1`] = ` | ||||||
| "const _Vue = Vue | "const _Vue = Vue | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -32,7 +32,7 @@ import { | |||||||
|   FRAGMENT, |   FRAGMENT, | ||||||
|   RENDER_LIST |   RENDER_LIST | ||||||
| } from '../src/runtimeHelpers' | } from '../src/runtimeHelpers' | ||||||
| import { createElementWithCodegen } from './testUtils' | import { createElementWithCodegen, genFlagText } from './testUtils' | ||||||
| import { PatchFlags } from '@vue/shared' | import { PatchFlags } from '@vue/shared' | ||||||
| 
 | 
 | ||||||
| function createRoot(options: Partial<RootNode> = {}): RootNode { | function createRoot(options: Partial<RootNode> = {}): RootNode { | ||||||
| @ -283,7 +283,7 @@ describe('compiler: codegen', () => { | |||||||
|             type: NodeTypes.VNODE_CALL, |             type: NodeTypes.VNODE_CALL, | ||||||
|             tag: FRAGMENT, |             tag: FRAGMENT, | ||||||
|             isBlock: true, |             isBlock: true, | ||||||
|             isForBlock: true, |             disableTracking: true, | ||||||
|             props: undefined, |             props: undefined, | ||||||
|             children: createCallExpression(RENDER_LIST), |             children: createCallExpression(RENDER_LIST), | ||||||
|             patchFlag: '1', |             patchFlag: '1', | ||||||
| @ -298,6 +298,37 @@ describe('compiler: codegen', () => { | |||||||
|     expect(code).toMatchSnapshot() |     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[])', () => { |   test('Element (callExpression + objectExpression + TemplateChildNode[])', () => { | ||||||
|     const { code } = generate( |     const { code } = generate( | ||||||
|       createRoot({ |       createRoot({ | ||||||
|  | |||||||
| @ -62,7 +62,7 @@ export function createElementWithCodegen( | |||||||
|       dynamicProps, |       dynamicProps, | ||||||
|       directives: undefined, |       directives: undefined, | ||||||
|       isBlock: false, |       isBlock: false, | ||||||
|       isForBlock: false, |       disableTracking: false, | ||||||
|       loc: locStub |       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`] = ` | exports[`compiler: v-for codegen v-if + v-for 1`] = ` | ||||||
| "const _Vue = Vue | "const _Vue = Vue | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -560,13 +560,16 @@ describe('compiler: v-for', () => { | |||||||
|     function assertSharedCodegen( |     function assertSharedCodegen( | ||||||
|       node: ForCodegenNode, |       node: ForCodegenNode, | ||||||
|       keyed: boolean = false, |       keyed: boolean = false, | ||||||
|       customReturn: boolean = false |       customReturn: boolean = false, | ||||||
|  |       disableTracking: boolean = true | ||||||
|     ) { |     ) { | ||||||
|       expect(node).toMatchObject({ |       expect(node).toMatchObject({ | ||||||
|         type: NodeTypes.VNODE_CALL, |         type: NodeTypes.VNODE_CALL, | ||||||
|         tag: FRAGMENT, |         tag: FRAGMENT, | ||||||
|         isForBlock: true, |         disableTracking, | ||||||
|         patchFlag: keyed |         patchFlag: !disableTracking | ||||||
|  |           ? genFlagText(PatchFlags.STABLE_FRAGMENT) | ||||||
|  |           : keyed | ||||||
|             ? genFlagText(PatchFlags.KEYED_FRAGMENT) |             ? genFlagText(PatchFlags.KEYED_FRAGMENT) | ||||||
|             : genFlagText(PatchFlags.UNKEYED_FRAGMENT), |             : genFlagText(PatchFlags.UNKEYED_FRAGMENT), | ||||||
|         children: { |         children: { | ||||||
| @ -580,7 +583,7 @@ describe('compiler: v-for', () => { | |||||||
|                 ? {} |                 ? {} | ||||||
|                 : { |                 : { | ||||||
|                     type: NodeTypes.VNODE_CALL, |                     type: NodeTypes.VNODE_CALL, | ||||||
|                     isBlock: true |                     isBlock: disableTracking | ||||||
|                   } |                   } | ||||||
|             } |             } | ||||||
|           ] |           ] | ||||||
| @ -658,6 +661,43 @@ describe('compiler: v-for', () => { | |||||||
|       expect(generate(root).code).toMatchSnapshot() |       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', () => { |     test('template v-for', () => { | ||||||
|       const { |       const { | ||||||
|         root, |         root, | ||||||
| @ -777,7 +817,7 @@ describe('compiler: v-for', () => { | |||||||
|             key: `[0]` |             key: `[0]` | ||||||
|           }), |           }), | ||||||
|           isBlock: true, |           isBlock: true, | ||||||
|           isForBlock: true, |           disableTracking: true, | ||||||
|           patchFlag: genFlagText(PatchFlags.UNKEYED_FRAGMENT), |           patchFlag: genFlagText(PatchFlags.UNKEYED_FRAGMENT), | ||||||
|           children: { |           children: { | ||||||
|             type: NodeTypes.JS_CALL_EXPRESSION, |             type: NodeTypes.JS_CALL_EXPRESSION, | ||||||
|  | |||||||
| @ -281,7 +281,7 @@ export interface VNodeCall extends Node { | |||||||
|   dynamicProps: string | undefined |   dynamicProps: string | undefined | ||||||
|   directives: DirectiveArguments | undefined |   directives: DirectiveArguments | undefined | ||||||
|   isBlock: boolean |   isBlock: boolean | ||||||
|   isForBlock: boolean |   disableTracking: boolean | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // JS Node Types ---------------------------------------------------------------
 | // JS Node Types ---------------------------------------------------------------
 | ||||||
| @ -492,7 +492,7 @@ export interface ForCodegenNode extends VNodeCall { | |||||||
|   props: undefined |   props: undefined | ||||||
|   children: ForRenderListExpression |   children: ForRenderListExpression | ||||||
|   patchFlag: string |   patchFlag: string | ||||||
|   isForBlock: true |   disableTracking: boolean | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface ForRenderListExpression extends CallExpression { | export interface ForRenderListExpression extends CallExpression { | ||||||
| @ -543,7 +543,7 @@ export function createVNodeCall( | |||||||
|   dynamicProps?: VNodeCall['dynamicProps'], |   dynamicProps?: VNodeCall['dynamicProps'], | ||||||
|   directives?: VNodeCall['directives'], |   directives?: VNodeCall['directives'], | ||||||
|   isBlock: VNodeCall['isBlock'] = false, |   isBlock: VNodeCall['isBlock'] = false, | ||||||
|   isForBlock: VNodeCall['isForBlock'] = false, |   disableTracking: VNodeCall['disableTracking'] = false, | ||||||
|   loc = locStub |   loc = locStub | ||||||
| ): VNodeCall { | ): VNodeCall { | ||||||
|   if (context) { |   if (context) { | ||||||
| @ -567,7 +567,7 @@ export function createVNodeCall( | |||||||
|     dynamicProps, |     dynamicProps, | ||||||
|     directives, |     directives, | ||||||
|     isBlock, |     isBlock, | ||||||
|     isForBlock, |     disableTracking, | ||||||
|     loc |     loc | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -698,13 +698,13 @@ function genVNodeCall(node: VNodeCall, context: CodegenContext) { | |||||||
|     dynamicProps, |     dynamicProps, | ||||||
|     directives, |     directives, | ||||||
|     isBlock, |     isBlock, | ||||||
|     isForBlock |     disableTracking | ||||||
|   } = node |   } = node | ||||||
|   if (directives) { |   if (directives) { | ||||||
|     push(helper(WITH_DIRECTIVES) + `(`) |     push(helper(WITH_DIRECTIVES) + `(`) | ||||||
|   } |   } | ||||||
|   if (isBlock) { |   if (isBlock) { | ||||||
|     push(`(${helper(OPEN_BLOCK)}(${isForBlock ? `true` : ``}), `) |     push(`(${helper(OPEN_BLOCK)}(${disableTracking ? `true` : ``}), `) | ||||||
|   } |   } | ||||||
|   if (pure) { |   if (pure) { | ||||||
|     push(PURE_ANNOTATION) |     push(PURE_ANNOTATION) | ||||||
|  | |||||||
| @ -203,7 +203,7 @@ export const transformElement: NodeTransform = (node, context) => { | |||||||
|       vnodeDynamicProps, |       vnodeDynamicProps, | ||||||
|       vnodeDirectives, |       vnodeDirectives, | ||||||
|       !!shouldUseBlock, |       !!shouldUseBlock, | ||||||
|       false /* isForBlock */, |       false /* disableTracking */, | ||||||
|       node.loc |       node.loc | ||||||
|     ) |     ) | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -55,7 +55,12 @@ export const transformFor = createStructuralDirectiveTransform( | |||||||
|         forNode.source |         forNode.source | ||||||
|       ]) as ForRenderListExpression |       ]) as ForRenderListExpression | ||||||
|       const keyProp = findProp(node, `key`) |       const keyProp = findProp(node, `key`) | ||||||
|       const fragmentFlag = keyProp |       const isStableFragment = | ||||||
|  |         forNode.source.type === NodeTypes.SIMPLE_EXPRESSION && | ||||||
|  |         forNode.source.isConstant | ||||||
|  |       const fragmentFlag = isStableFragment | ||||||
|  |         ? PatchFlags.STABLE_FRAGMENT | ||||||
|  |         : keyProp | ||||||
|           ? PatchFlags.KEYED_FRAGMENT |           ? PatchFlags.KEYED_FRAGMENT | ||||||
|           : PatchFlags.UNKEYED_FRAGMENT |           : PatchFlags.UNKEYED_FRAGMENT | ||||||
|       forNode.codegenNode = createVNodeCall( |       forNode.codegenNode = createVNodeCall( | ||||||
| @ -67,7 +72,7 @@ export const transformFor = createStructuralDirectiveTransform( | |||||||
|         undefined, |         undefined, | ||||||
|         undefined, |         undefined, | ||||||
|         true /* isBlock */, |         true /* isBlock */, | ||||||
|         true /* isForBlock */, |         !isStableFragment /* disableTracking */, | ||||||
|         node.loc |         node.loc | ||||||
|       ) as ForCodegenNode |       ) as ForCodegenNode | ||||||
| 
 | 
 | ||||||
| @ -122,10 +127,12 @@ export const transformFor = createStructuralDirectiveTransform( | |||||||
|           // but mark it as a block.
 |           // but mark it as a block.
 | ||||||
|           childBlock = (children[0] as PlainElementNode) |           childBlock = (children[0] as PlainElementNode) | ||||||
|             .codegenNode as VNodeCall |             .codegenNode as VNodeCall | ||||||
|           childBlock.isBlock = true |           childBlock.isBlock = !isStableFragment | ||||||
|  |           if (childBlock.isBlock) { | ||||||
|             helper(OPEN_BLOCK) |             helper(OPEN_BLOCK) | ||||||
|             helper(CREATE_BLOCK) |             helper(CREATE_BLOCK) | ||||||
|           } |           } | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         renderExp.arguments.push(createFunctionExpression( |         renderExp.arguments.push(createFunctionExpression( | ||||||
|           createForLoopParams(forNode.parseResult), |           createForLoopParams(forNode.parseResult), | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user