]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
perf(compiler-core): treat v-for with constant exp as a stable fragment (#1394)
authorHcySunYang <HcySunYang@outlook.com>
Wed, 17 Jun 2020 20:13:14 +0000 (04:13 +0800)
committerGitHub <noreply@github.com>
Wed, 17 Jun 2020 20:13:14 +0000 (16:13 -0400)
packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap
packages/compiler-core/__tests__/codegen.spec.ts
packages/compiler-core/__tests__/testUtils.ts
packages/compiler-core/__tests__/transforms/__snapshots__/vFor.spec.ts.snap
packages/compiler-core/__tests__/transforms/vFor.spec.ts
packages/compiler-core/src/ast.ts
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/transforms/transformElement.ts
packages/compiler-core/src/transforms/vFor.ts

index 68990eccef168c8700ab8bf33af66ff7641012ec..19a152de79bcc6d5452b240cd0b70fb72848dc8c 100644 (file)
@@ -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
 
index f9ebdc09fc9c22758c4aa1a1b21fb1338fbd0875..2fc1bf93e8cf2db536d47121154398c43516ef61 100644 (file)
@@ -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({
index 300873facc72f27e7666877b7e3b803ba5dcc1c0..61d2d8d0fce075188a9251792373618b48fd755e 100644 (file)
@@ -62,7 +62,7 @@ export function createElementWithCodegen(
       dynamicProps,
       directives: undefined,
       isBlock: false,
-      isForBlock: false,
+      disableTracking: false,
       loc: locStub
     }
   }
index 870922105c05f4589bd235914929e6f9fdf1c192..970048cf699e17e46ab79d869934a91b08e703d0 100644 (file)
@@ -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
 
index 03d96cdf25c1845d754f3be076266425511bea85..2657c08fe9711a2b9e79b71fb332e6c4d4835dc8 100644 (file)
@@ -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,
index d4c11c07b9efb1be98e79ca1bccf976655bef37a..6b85e150c4e85d78737e4c4f365af3c52acecb0a 100644 (file)
@@ -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
   }
 }
index d95b22b6bd76835b2c50eb8bf804dc1da1d76605..b213eec0ff75f0a8e67298cb59e00bdd654833ae 100644 (file)
@@ -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)
index 012b3801af0d49dbb3417aabcc6d3e4d7f5ef864..4daf7a0600bcc0605bd2974d1d994c2db847f478 100644 (file)
@@ -203,7 +203,7 @@ export const transformElement: NodeTransform = (node, context) => {
       vnodeDynamicProps,
       vnodeDirectives,
       !!shouldUseBlock,
-      false /* isForBlock */,
+      false /* disableTracking */,
       node.loc
     )
   }
index ee5c243a80019c4a453e4195fb6f3eaa512bb52d..d85964e5270e97b386084ddd903619901cc3126d 100644 (file)
@@ -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(