]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip(compiler): codegen node w/ block optimization for v-for
authorEvan You <yyx990803@gmail.com>
Tue, 1 Oct 2019 20:48:20 +0000 (16:48 -0400)
committerEvan You <yyx990803@gmail.com>
Tue, 1 Oct 2019 20:48:20 +0000 (16:48 -0400)
packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap
packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap
packages/compiler-core/__tests__/codegen.spec.ts
packages/compiler-core/__tests__/transforms/vSlot.spec.ts
packages/compiler-core/src/ast.ts
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/transforms/vFor.ts
packages/compiler-core/src/transforms/vIf.ts
packages/compiler-core/src/transforms/vSlot.ts

index 2d41b784b8d1ff462d793f232d711c2e5dadb57f..628f352e65b2fee6cf7c863412075e0ececc2956 100644 (file)
@@ -22,19 +22,6 @@ return function render() {
 }"
 `;
 
-exports[`compiler: codegen SlotFunctionExpression 1`] = `
-"
-return function render() {
-  with (this) {
-    return _createVNode(Comp, 0, {
-      default: ({ foo }) => [
-        _toString(foo)
-      ]
-    })
-  }
-}"
-`;
-
 exports[`compiler: codegen callExpression + objectExpression + arrayExpression 1`] = `
 "
 return function render() {
@@ -71,60 +58,6 @@ return function render() {
 }"
 `;
 
-exports[`compiler: codegen forNode 1`] = `
-"
-return function render() {
-  with (this) {
-    return _renderList(list, (v, k, i) => {
-      return _toString(v)
-    })
-  }
-}"
-`;
-
-exports[`compiler: codegen forNode w/ prefixIdentifiers: true 1`] = `
-"
-return function render() {
-  const _ctx = this
-  return renderList(list, (v, k, i) => {
-    return toString(v)
-  })
-}"
-`;
-
-exports[`compiler: codegen forNode w/ skipped key alias 1`] = `
-"
-return function render() {
-  with (this) {
-    return _renderList(list, (v, __key, i) => {
-      return _toString(v)
-    })
-  }
-}"
-`;
-
-exports[`compiler: codegen forNode w/ skipped value alias 1`] = `
-"
-return function render() {
-  with (this) {
-    return _renderList(list, (__value, k, i) => {
-      return _toString(v)
-    })
-  }
-}"
-`;
-
-exports[`compiler: codegen forNode w/ skipped value and key aliases 1`] = `
-"
-return function render() {
-  with (this) {
-    return _renderList(list, (__value, __key, i) => {
-      return _toString(v)
-    })
-  }
-}"
-`;
-
 exports[`compiler: codegen function mode preamble 1`] = `
 "const _Vue = Vue
 
index 359a881ddca59c6e6d9675f960c9e60140951122..1ac0c95bc5022d2ec10347ca01dd9b5090a7bbb7 100644 (file)
@@ -15,11 +15,11 @@ return function render() {
       (_openBlock(), ok
         ? _createBlock(\\"div\\", { key: 0 }, \\"yes\\")
         : _createBlock(_Fragment, { key: 1 }, \\"no\\")),
-      _renderList(list, (value, index) => {
+      (_openBlock(), _createBlock(_Fragment, null, _renderList(list, (value, index) => {
         return _createVNode(\\"div\\", null, [
           _createVNode(\\"span\\", null, _toString(value + index))
         ])
-      })
+      })))
     ], 2)
   }
 }"
@@ -38,11 +38,11 @@ return function render() {
     (openBlock(), (_ctx.ok)
       ? createBlock(\\"div\\", { key: 0 }, \\"yes\\")
       : createBlock(Fragment, { key: 1 }, \\"no\\")),
-    renderList(_ctx.list, (value, index) => {
+    (openBlock(), createBlock(Fragment, null, renderList(_ctx.list, (value, index) => {
       return createVNode(\\"div\\", null, [
         createVNode(\\"span\\", null, toString(value + index))
       ])
-    })
+    })))
   ], 2)
 }"
 `;
@@ -60,11 +60,11 @@ export default function render() {
     (openBlock(), (_ctx.ok)
       ? createBlock(\\"div\\", { key: 0 }, \\"yes\\")
       : createBlock(Fragment, { key: 1 }, \\"no\\")),
-    _renderList(_ctx.list, (value, index) => {
+    (openBlock(), createBlock(Fragment, null, renderList(_ctx.list, (value, index) => {
       return createVNode(\\"div\\", null, [
         createVNode(\\"span\\", null, _toString(value + index))
       ])
-    })
+    })))
   ], 2)
 }"
 `;
index d8e752bf0687a7d179ddf5294d9f77eaaa7e162c..469e6ed3a0197a8d61ff55734585e1e913843696 100644 (file)
@@ -4,8 +4,6 @@ import {
   NodeTypes,
   RootNode,
   createSimpleExpression,
-  Namespaces,
-  ElementTypes,
   createObjectExpression,
   createObjectProperty,
   createArrayExpression,
@@ -15,12 +13,7 @@ import {
   createCallExpression,
   createConditionalExpression
 } from '../src'
-import {
-  CREATE_VNODE,
-  COMMENT,
-  TO_STRING,
-  RENDER_LIST
-} from '../src/runtimeConstants'
+import { CREATE_VNODE, COMMENT, TO_STRING } from '../src/runtimeConstants'
 import { createElementWithCodegen } from './testUtils'
 
 function createRoot(options: Partial<RootNode> = {}): RootNode {
@@ -250,202 +243,202 @@ describe('compiler: codegen', () => {
     expect(code).toMatchSnapshot()
   })
 
-  test('forNode', () => {
-    const { code } = generate(
-      createRoot({
-        children: [
-          {
-            type: NodeTypes.FOR,
-            loc: locStub,
-            source: createSimpleExpression(`list`, false, locStub),
-            valueAlias: createSimpleExpression(`v`, false, locStub),
-            keyAlias: createSimpleExpression(`k`, false, locStub),
-            objectIndexAlias: createSimpleExpression(`i`, false, locStub),
-            children: [createInterpolation(`v`, locStub)]
-          }
-        ]
-      })
-    )
-    expect(code).toMatch(
-      `return _${RENDER_LIST}(list, (v, k, i) => {
-      return _${TO_STRING}(v)
-    })`
-    )
-    expect(code).toMatchSnapshot()
-  })
+  // test('forNode', () => {
+  //   const { code } = generate(
+  //     createRoot({
+  //       children: [
+  //         {
+  //           type: NodeTypes.FOR,
+  //           loc: locStub,
+  //           source: createSimpleExpression(`list`, false, locStub),
+  //           valueAlias: createSimpleExpression(`v`, false, locStub),
+  //           keyAlias: createSimpleExpression(`k`, false, locStub),
+  //           objectIndexAlias: createSimpleExpression(`i`, false, locStub),
+  //           children: [createInterpolation(`v`, locStub)]
+  //         }
+  //       ]
+  //     })
+  //   )
+  //   expect(code).toMatch(
+  //     `return _${RENDER_LIST}(list, (v, k, i) => {
+  //     return _${TO_STRING}(v)
+  //   })`
+  //   )
+  //   expect(code).toMatchSnapshot()
+  // })
 
-  test('forNode w/ prefixIdentifiers: true', () => {
-    const { code } = generate(
-      createRoot({
-        children: [
-          {
-            type: NodeTypes.FOR,
-            loc: locStub,
-            source: createSimpleExpression(`list`, false, locStub),
-            valueAlias: createSimpleExpression(`v`, false, locStub),
-            keyAlias: createSimpleExpression(`k`, false, locStub),
-            objectIndexAlias: createSimpleExpression(`i`, false, locStub),
-            children: [createInterpolation(`v`, locStub)]
-          }
-        ]
-      }),
-      {
-        prefixIdentifiers: true
-      }
-    )
-    expect(code).toMatch(
-      `return ${RENDER_LIST}(list, (v, k, i) => {
-    return ${TO_STRING}(v)
-  })`
-    )
-    expect(code).toMatchSnapshot()
-  })
+  // test('forNode w/ prefixIdentifiers: true', () => {
+  //   const { code } = generate(
+  //     createRoot({
+  //       children: [
+  //         {
+  //           type: NodeTypes.FOR,
+  //           loc: locStub,
+  //           source: createSimpleExpression(`list`, false, locStub),
+  //           valueAlias: createSimpleExpression(`v`, false, locStub),
+  //           keyAlias: createSimpleExpression(`k`, false, locStub),
+  //           objectIndexAlias: createSimpleExpression(`i`, false, locStub),
+  //           children: [createInterpolation(`v`, locStub)]
+  //         }
+  //       ]
+  //     }),
+  //     {
+  //       prefixIdentifiers: true
+  //     }
+  //   )
+  //   expect(code).toMatch(
+  //     `return ${RENDER_LIST}(list, (v, k, i) => {
+  //   return ${TO_STRING}(v)
+  // })`
+  //   )
+  //   expect(code).toMatchSnapshot()
+  // })
 
-  test('forNode w/ skipped value alias', () => {
-    const { code } = generate(
-      createRoot({
-        children: [
-          {
-            type: NodeTypes.FOR,
-            loc: locStub,
-            source: createSimpleExpression(`list`, false, locStub),
-            valueAlias: undefined,
-            keyAlias: createSimpleExpression(`k`, false, locStub),
-            objectIndexAlias: createSimpleExpression(`i`, false, locStub),
-            children: [createInterpolation(`v`, locStub)]
-          }
-        ]
-      })
-    )
-    expect(code).toMatch(
-      `return _${RENDER_LIST}(list, (__value, k, i) => {
-      return _${TO_STRING}(v)
-    })`
-    )
-    expect(code).toMatchSnapshot()
-  })
+  // test('forNode w/ skipped value alias', () => {
+  //   const { code } = generate(
+  //     createRoot({
+  //       children: [
+  //         {
+  //           type: NodeTypes.FOR,
+  //           loc: locStub,
+  //           source: createSimpleExpression(`list`, false, locStub),
+  //           valueAlias: undefined,
+  //           keyAlias: createSimpleExpression(`k`, false, locStub),
+  //           objectIndexAlias: createSimpleExpression(`i`, false, locStub),
+  //           children: [createInterpolation(`v`, locStub)]
+  //         }
+  //       ]
+  //     })
+  //   )
+  //   expect(code).toMatch(
+  //     `return _${RENDER_LIST}(list, (__value, k, i) => {
+  //     return _${TO_STRING}(v)
+  //   })`
+  //   )
+  //   expect(code).toMatchSnapshot()
+  // })
 
-  test('forNode w/ skipped key alias', () => {
-    const { code } = generate(
-      createRoot({
-        children: [
-          {
-            type: NodeTypes.FOR,
-            loc: locStub,
-            source: createSimpleExpression(`list`, false, locStub),
-            valueAlias: createSimpleExpression(`v`, false, locStub),
-            keyAlias: undefined,
-            objectIndexAlias: createSimpleExpression(`i`, false, locStub),
-            children: [createInterpolation(`v`, locStub)]
-          }
-        ]
-      })
-    )
-    expect(code).toMatch(
-      `return _${RENDER_LIST}(list, (v, __key, i) => {
-      return _${TO_STRING}(v)
-    })`
-    )
-    expect(code).toMatchSnapshot()
-  })
+  // test('forNode w/ skipped key alias', () => {
+  //   const { code } = generate(
+  //     createRoot({
+  //       children: [
+  //         {
+  //           type: NodeTypes.FOR,
+  //           loc: locStub,
+  //           source: createSimpleExpression(`list`, false, locStub),
+  //           valueAlias: createSimpleExpression(`v`, false, locStub),
+  //           keyAlias: undefined,
+  //           objectIndexAlias: createSimpleExpression(`i`, false, locStub),
+  //           children: [createInterpolation(`v`, locStub)]
+  //         }
+  //       ]
+  //     })
+  //   )
+  //   expect(code).toMatch(
+  //     `return _${RENDER_LIST}(list, (v, __key, i) => {
+  //     return _${TO_STRING}(v)
+  //   })`
+  //   )
+  //   expect(code).toMatchSnapshot()
+  // })
 
-  test('forNode w/ skipped value and key aliases', () => {
-    const { code } = generate(
-      createRoot({
-        children: [
-          {
-            type: NodeTypes.FOR,
-            loc: locStub,
-            source: createSimpleExpression(`list`, false, locStub),
-            valueAlias: undefined,
-            keyAlias: undefined,
-            objectIndexAlias: createSimpleExpression(`i`, false, locStub),
-            children: [createInterpolation(`v`, locStub)]
-          }
-        ]
-      })
-    )
-    expect(code).toMatch(
-      `return _${RENDER_LIST}(list, (__value, __key, i) => {
-      return _${TO_STRING}(v)
-    })`
-    )
-    expect(code).toMatchSnapshot()
-  })
+  // test('forNode w/ skipped value and key aliases', () => {
+  //   const { code } = generate(
+  //     createRoot({
+  //       children: [
+  //         {
+  //           type: NodeTypes.FOR,
+  //           loc: locStub,
+  //           source: createSimpleExpression(`list`, false, locStub),
+  //           valueAlias: undefined,
+  //           keyAlias: undefined,
+  //           objectIndexAlias: createSimpleExpression(`i`, false, locStub),
+  //           children: [createInterpolation(`v`, locStub)]
+  //         }
+  //       ]
+  //     })
+  //   )
+  //   expect(code).toMatch(
+  //     `return _${RENDER_LIST}(list, (__value, __key, i) => {
+  //     return _${TO_STRING}(v)
+  //   })`
+  //   )
+  //   expect(code).toMatchSnapshot()
+  // })
 
-  test('SlotFunctionExpression', () => {
-    const { code } = generate(
-      createRoot({
-        children: [
-          {
-            type: NodeTypes.ELEMENT,
-            tagType: ElementTypes.COMPONENT,
-            ns: Namespaces.HTML,
-            isSelfClosing: false,
-            tag: `Comp`,
-            loc: locStub,
-            props: [],
-            children: [],
-            codegenNode: {
-              type: NodeTypes.JS_CALL_EXPRESSION,
-              loc: locStub,
-              callee: `_${CREATE_VNODE}`,
-              arguments: [
-                `Comp`,
-                `0`,
-                {
-                  type: NodeTypes.JS_OBJECT_EXPRESSION,
-                  loc: locStub,
-                  properties: [
-                    {
-                      type: NodeTypes.JS_PROPERTY,
-                      loc: locStub,
-                      key: {
-                        type: NodeTypes.SIMPLE_EXPRESSION,
-                        isStatic: true,
-                        content: `default`,
-                        loc: locStub
-                      },
-                      value: {
-                        type: NodeTypes.JS_SLOT_FUNCTION,
-                        loc: locStub,
-                        params: {
-                          type: NodeTypes.SIMPLE_EXPRESSION,
-                          isStatic: false,
-                          content: `{ foo }`,
-                          loc: locStub
-                        },
-                        returns: [
-                          {
-                            type: NodeTypes.INTERPOLATION,
-                            loc: locStub,
-                            content: {
-                              type: NodeTypes.SIMPLE_EXPRESSION,
-                              isStatic: false,
-                              content: `foo`,
-                              loc: locStub
-                            }
-                          }
-                        ]
-                      }
-                    }
-                  ]
-                }
-              ]
-            }
-          }
-        ]
-      })
-    )
-    expect(code).toMatch(
-      `return _createVNode(Comp, 0, {
-      default: ({ foo }) => [
-        _toString(foo)
-      ]
-    })`
-    )
-    expect(code).toMatchSnapshot()
-  })
+  // test('SlotFunctionExpression', () => {
+  //   const { code } = generate(
+  //     createRoot({
+  //       children: [
+  //         {
+  //           type: NodeTypes.ELEMENT,
+  //           tagType: ElementTypes.COMPONENT,
+  //           ns: Namespaces.HTML,
+  //           isSelfClosing: false,
+  //           tag: `Comp`,
+  //           loc: locStub,
+  //           props: [],
+  //           children: [],
+  //           codegenNode: {
+  //             type: NodeTypes.JS_CALL_EXPRESSION,
+  //             loc: locStub,
+  //             callee: `_${CREATE_VNODE}`,
+  //             arguments: [
+  //               `Comp`,
+  //               `0`,
+  //               {
+  //                 type: NodeTypes.JS_OBJECT_EXPRESSION,
+  //                 loc: locStub,
+  //                 properties: [
+  //                   {
+  //                     type: NodeTypes.JS_PROPERTY,
+  //                     loc: locStub,
+  //                     key: {
+  //                       type: NodeTypes.SIMPLE_EXPRESSION,
+  //                       isStatic: true,
+  //                       content: `default`,
+  //                       loc: locStub
+  //                     },
+  //                     value: {
+  //                       type: NodeTypes.JS_FUNCTION_EXPRESSION,
+  //                       loc: locStub,
+  //                       params: {
+  //                         type: NodeTypes.SIMPLE_EXPRESSION,
+  //                         isStatic: false,
+  //                         content: `{ foo }`,
+  //                         loc: locStub
+  //                       },
+  //                       returns: [
+  //                         {
+  //                           type: NodeTypes.INTERPOLATION,
+  //                           loc: locStub,
+  //                           content: {
+  //                             type: NodeTypes.SIMPLE_EXPRESSION,
+  //                             isStatic: false,
+  //                             content: `foo`,
+  //                             loc: locStub
+  //                           }
+  //                         }
+  //                       ]
+  //                     }
+  //                   }
+  //                 ]
+  //               }
+  //             ]
+  //           }
+  //         }
+  //       ]
+  //     })
+  //   )
+  //   expect(code).toMatch(
+  //     `return _createVNode(Comp, 0, {
+  //     default: ({ foo }) => [
+  //       _toString(foo)
+  //     ]
+  //   })`
+  //   )
+  //   expect(code).toMatchSnapshot()
+  // })
 
   test('callExpression + objectExpression + arrayExpression', () => {
     const { code } = generate(
index bfd4a68ae88c807e8b27b7f016296778dc739ff7..93fc5329c5b211a6af69dc0a776a8255d57bd5d0 100644 (file)
@@ -59,7 +59,7 @@ describe('compiler: transform component slots', () => {
     expect(slots).toMatchObject(
       createSlotMatcher({
         default: {
-          type: NodeTypes.JS_SLOT_FUNCTION,
+          type: NodeTypes.JS_FUNCTION_EXPRESSION,
           params: undefined,
           returns: [
             {
@@ -81,7 +81,7 @@ describe('compiler: transform component slots', () => {
     expect(slots).toMatchObject(
       createSlotMatcher({
         default: {
-          type: NodeTypes.JS_SLOT_FUNCTION,
+          type: NodeTypes.JS_FUNCTION_EXPRESSION,
           params: {
             type: NodeTypes.COMPOUND_EXPRESSION,
             children: [`{ `, { content: `foo` }, ` }`]
@@ -121,7 +121,7 @@ describe('compiler: transform component slots', () => {
     expect(slots).toMatchObject(
       createSlotMatcher({
         one: {
-          type: NodeTypes.JS_SLOT_FUNCTION,
+          type: NodeTypes.JS_FUNCTION_EXPRESSION,
           params: {
             type: NodeTypes.SIMPLE_EXPRESSION,
             content: `{ foo }`,
@@ -143,7 +143,7 @@ describe('compiler: transform component slots', () => {
           ]
         },
         two: {
-          type: NodeTypes.JS_SLOT_FUNCTION,
+          type: NodeTypes.JS_FUNCTION_EXPRESSION,
           params: {
             type: NodeTypes.SIMPLE_EXPRESSION,
             content: `{ bar }`,
@@ -184,7 +184,7 @@ describe('compiler: transform component slots', () => {
     expect(slots).toMatchObject(
       createSlotMatcher({
         '[_ctx.one]': {
-          type: NodeTypes.JS_SLOT_FUNCTION,
+          type: NodeTypes.JS_FUNCTION_EXPRESSION,
           params: {
             type: NodeTypes.SIMPLE_EXPRESSION,
             content: `{ foo }`,
@@ -206,7 +206,7 @@ describe('compiler: transform component slots', () => {
           ]
         },
         '[_ctx.two]': {
-          type: NodeTypes.JS_SLOT_FUNCTION,
+          type: NodeTypes.JS_FUNCTION_EXPRESSION,
           params: {
             type: NodeTypes.SIMPLE_EXPRESSION,
             content: `{ bar }`,
@@ -247,7 +247,7 @@ describe('compiler: transform component slots', () => {
     expect(slots).toMatchObject(
       createSlotMatcher({
         default: {
-          type: NodeTypes.JS_SLOT_FUNCTION,
+          type: NodeTypes.JS_FUNCTION_EXPRESSION,
           params: {
             type: NodeTypes.SIMPLE_EXPRESSION,
             content: `{ foo }`,
@@ -263,7 +263,7 @@ describe('compiler: transform component slots', () => {
                   `null`,
                   createSlotMatcher({
                     default: {
-                      type: NodeTypes.JS_SLOT_FUNCTION,
+                      type: NodeTypes.JS_FUNCTION_EXPRESSION,
                       params: {
                         type: NodeTypes.COMPOUND_EXPRESSION,
                         children: [`{ `, { content: `bar` }, ` }`]
index effd98e04315994a89697c572761e87ad0f563c9..cc8f4296ac1154278d262179ab8ef89ce291eb87 100644 (file)
@@ -28,7 +28,7 @@ export const enum NodeTypes {
   JS_OBJECT_EXPRESSION,
   JS_PROPERTY,
   JS_ARRAY_EXPRESSION,
-  JS_SLOT_FUNCTION,
+  JS_FUNCTION_EXPRESSION,
   JS_SEQUENCE_EXPRESSION,
   JS_CONDITIONAL_EXPRESSION
 }
@@ -158,6 +158,7 @@ export interface ForNode extends Node {
   keyAlias: ExpressionNode | undefined
   objectIndexAlias: ExpressionNode | undefined
   children: TemplateChildNode[]
+  codegenNode: SequenceExpression
 }
 
 // We also include a number of JavaScript AST nodes for code generation.
@@ -168,7 +169,7 @@ export type JSChildNode =
   | ObjectExpression
   | ArrayExpression
   | ExpressionNode
-  | SlotFunctionExpression
+  | FunctionExpression
   | ConditionalExpression
   | SequenceExpression
 
@@ -194,10 +195,11 @@ export interface ArrayExpression extends Node {
   elements: Array<string | JSChildNode>
 }
 
-export interface SlotFunctionExpression extends Node {
-  type: NodeTypes.JS_SLOT_FUNCTION
-  params: ExpressionNode | undefined
-  returns: TemplateChildNode[]
+export interface FunctionExpression extends Node {
+  type: NodeTypes.JS_FUNCTION_EXPRESSION
+  params: ExpressionNode | ExpressionNode[] | undefined
+  returns: TemplateChildNode | TemplateChildNode[]
+  newline: boolean
 }
 
 export interface SequenceExpression extends Node {
@@ -235,7 +237,7 @@ export function createArrayExpression(
 }
 
 export function createObjectExpression(
-  properties: Property[],
+  properties: ObjectExpression['properties'],
   loc: SourceLocation = locStub
 ): ObjectExpression {
   return {
@@ -246,8 +248,8 @@ export function createObjectExpression(
 }
 
 export function createObjectProperty(
-  key: ExpressionNode,
-  value: JSChildNode
+  key: Property['key'],
+  value: Property['value']
 ): Property {
   return {
     type: NodeTypes.JS_PROPERTY,
@@ -258,8 +260,8 @@ export function createObjectProperty(
 }
 
 export function createSimpleExpression(
-  content: string,
-  isStatic: boolean,
+  content: SimpleExpressionNode['content'],
+  isStatic: SimpleExpressionNode['isStatic'],
   loc: SourceLocation = locStub
 ): SimpleExpressionNode {
   return {
@@ -271,7 +273,7 @@ export function createSimpleExpression(
 }
 
 export function createInterpolation(
-  content: string | CompoundExpressionNode,
+  content: InterpolationNode['content'] | string,
   loc: SourceLocation
 ): InterpolationNode {
   return {
@@ -294,7 +296,7 @@ export function createCompoundExpression(
 }
 
 export function createCallExpression(
-  callee: string,
+  callee: CallExpression['callee'],
   args: CallExpression['arguments'] = [],
   loc: SourceLocation = locStub
 ): CallExpression {
@@ -307,20 +309,22 @@ export function createCallExpression(
 }
 
 export function createFunctionExpression(
-  params: ExpressionNode | undefined,
-  returns: TemplateChildNode[],
+  params: FunctionExpression['params'],
+  returns: FunctionExpression['returns'],
+  newline: boolean = false,
   loc: SourceLocation = locStub
-): SlotFunctionExpression {
+): FunctionExpression {
   return {
-    type: NodeTypes.JS_SLOT_FUNCTION,
+    type: NodeTypes.JS_FUNCTION_EXPRESSION,
     params,
     returns,
+    newline,
     loc
   }
 }
 
 export function createSequenceExpression(
-  expressions: JSChildNode[]
+  expressions: SequenceExpression['expressions']
 ): SequenceExpression {
   return {
     type: NodeTypes.JS_SEQUENCE_EXPRESSION,
@@ -330,9 +334,9 @@ export function createSequenceExpression(
 }
 
 export function createConditionalExpression(
-  test: ExpressionNode,
-  consequent: JSChildNode,
-  alternate: JSChildNode
+  test: ConditionalExpression['test'],
+  consequent: ConditionalExpression['consequent'],
+  alternate: ConditionalExpression['alternate']
 ): ConditionalExpression {
   return {
     type: NodeTypes.JS_CONDITIONAL_EXPRESSION,
index 0875573e04e03fd718c675a7e965577bdd25e37a..c25518c14b23cfcc27ea1e3272f20bbffa2651b8 100644 (file)
@@ -2,8 +2,6 @@ import {
   RootNode,
   TemplateChildNode,
   ElementNode,
-  IfNode,
-  ForNode,
   TextNode,
   CommentNode,
   ExpressionNode,
@@ -18,7 +16,7 @@ import {
   CompoundExpressionNode,
   SimpleExpressionNode,
   ElementTypes,
-  SlotFunctionExpression,
+  FunctionExpression,
   SequenceExpression,
   ConditionalExpression
 } from './ast'
@@ -29,12 +27,7 @@ import {
   isSimpleIdentifier
 } from './utils'
 import { isString, isArray } from '@vue/shared'
-import {
-  RENDER_LIST,
-  TO_STRING,
-  CREATE_VNODE,
-  COMMENT
-} from './runtimeConstants'
+import { TO_STRING, CREATE_VNODE, COMMENT } from './runtimeConstants'
 
 type CodegenNode = TemplateChildNode | JSChildNode
 
@@ -342,7 +335,15 @@ function genNodeList(
 function genNode(node: CodegenNode, context: CodegenContext) {
   switch (node.type) {
     case NodeTypes.ELEMENT:
-      genElement(node, context)
+    case NodeTypes.IF:
+    case NodeTypes.FOR:
+      __DEV__ &&
+        assert(
+          node.codegenNode != null,
+          `Codegen node is missing for element/if/for node. ` +
+            `Apply appropriate transforms first.`
+        )
+      genNode(node.codegenNode!, context)
       break
     case NodeTypes.TEXT:
       genText(node, context)
@@ -359,12 +360,6 @@ function genNode(node: CodegenNode, context: CodegenContext) {
     case NodeTypes.COMMENT:
       genComment(node, context)
       break
-    case NodeTypes.IF:
-      genIf(node, context)
-      break
-    case NodeTypes.FOR:
-      genFor(node, context)
-      break
     case NodeTypes.JS_CALL_EXPRESSION:
       genCallExpression(node, context)
       break
@@ -374,8 +369,8 @@ function genNode(node: CodegenNode, context: CodegenContext) {
     case NodeTypes.JS_ARRAY_EXPRESSION:
       genArrayExpression(node, context)
       break
-    case NodeTypes.JS_SLOT_FUNCTION:
-      genSlotFunction(node, context)
+    case NodeTypes.JS_FUNCTION_EXPRESSION:
+      genFunctionExpression(node, context)
       break
     case NodeTypes.JS_SEQUENCE_EXPRESSION:
       genSequenceExpression(node, context)
@@ -394,16 +389,6 @@ function genNode(node: CodegenNode, context: CodegenContext) {
   }
 }
 
-function genElement(node: ElementNode, context: CodegenContext) {
-  __DEV__ &&
-    assert(
-      node.codegenNode != null,
-      `AST is not transformed for codegen. ` +
-        `Apply appropriate transforms first.`
-    )
-  genCallExpression(node.codegenNode!, context, false)
-}
-
 function genText(
   node: TextNode | SimpleExpressionNode,
   context: CodegenContext
@@ -469,56 +454,10 @@ function genComment(node: CommentNode, context: CodegenContext) {
   }
 }
 
-// control flow
-function genIf(node: IfNode, context: CodegenContext) {
-  genNode(node.codegenNode, context)
-}
-
-function genFor(node: ForNode, context: CodegenContext) {
-  const { push, helper, indent, deindent } = context
-  const { source, keyAlias, valueAlias, objectIndexAlias, children } = node
-  push(`${helper(RENDER_LIST)}(`, node, true)
-  genNode(source, context)
-  push(`, (`)
-  if (valueAlias) {
-    genNode(valueAlias, context)
-  }
-  if (keyAlias) {
-    if (!valueAlias) {
-      push(`__value`)
-    }
-    push(`, `)
-    genNode(keyAlias, context)
-  }
-  if (objectIndexAlias) {
-    if (!keyAlias) {
-      if (!valueAlias) {
-        push(`__value, __key`)
-      } else {
-        push(`, __key`)
-      }
-    }
-    push(`, `)
-    genNode(objectIndexAlias, context)
-  }
-  push(`) => {`)
-  indent()
-  push(`return `)
-  genChildren(children, context, true)
-  deindent()
-  push(`})`)
-}
-
 // JavaScript
-function genCallExpression(
-  node: CallExpression,
-  context: CodegenContext,
-  multilines = false
-) {
+function genCallExpression(node: CallExpression, context: CodegenContext) {
   context.push(node.callee + `(`, node, true)
-  multilines && context.indent()
-  genNodeList(node.arguments, context, multilines)
-  multilines && context.deindent()
+  genNodeList(node.arguments, context)
   context.push(`)`)
 }
 
@@ -554,15 +493,33 @@ function genArrayExpression(node: ArrayExpression, context: CodegenContext) {
   genNodeListAsArray(node.elements, context)
 }
 
-function genSlotFunction(
-  node: SlotFunctionExpression,
+function genFunctionExpression(
+  node: FunctionExpression,
   context: CodegenContext
 ) {
-  context.push(`(`, node)
-  if (node.params) genNode(node.params, context)
-  context.push(`) => `)
-  // pre-normalized slots should always return arrays
-  genNodeListAsArray(node.returns, context)
+  const { push, indent, deindent } = context
+  const { params, returns, newline } = node
+  push(`(`, node)
+  if (isArray(params)) {
+    genNodeList(params, context)
+  } else if (params) {
+    genNode(params, context)
+  }
+  push(`) => `)
+  if (newline) {
+    push(`{`)
+    indent()
+    push(`return `)
+  }
+  if (isArray(returns)) {
+    genNodeListAsArray(returns, context)
+  } else {
+    genNode(returns, context)
+  }
+  if (newline) {
+    deindent()
+    push(`}`)
+  }
 }
 
 function genConditionalExpression(
index ddfca31f5e9e27b1c855bf3385133e77e578c927..0f3a9aedf539eeae2b5db18c592e28106a5fb7ed 100644 (file)
@@ -7,11 +7,20 @@ import {
   ExpressionNode,
   createSimpleExpression,
   SourceLocation,
-  SimpleExpressionNode
+  SimpleExpressionNode,
+  createSequenceExpression,
+  createCallExpression,
+  createFunctionExpression,
+  ElementTypes
 } from '../ast'
 import { createCompilerError, ErrorCodes } from '../errors'
 import { getInnerRange } from '../utils'
-import { RENDER_LIST } from '../runtimeConstants'
+import {
+  RENDER_LIST,
+  OPEN_BLOCK,
+  CREATE_BLOCK,
+  FRAGMENT
+} from '../runtimeConstants'
 import { processExpression } from './transformExpression'
 
 export const transformFor = createStructuralDirectiveTransform(
@@ -26,9 +35,14 @@ export const transformFor = createStructuralDirectiveTransform(
       )
 
       if (parseResult) {
-        context.helper(RENDER_LIST)
+        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
+        ])
+
         context.replaceNode({
           type: NodeTypes.FOR,
           loc: dir.loc,
@@ -36,19 +50,52 @@ export const transformFor = createStructuralDirectiveTransform(
           valueAlias: value,
           keyAlias: key,
           objectIndexAlias: index,
-          children: [node]
+          children: [node],
+          codegenNode
         })
 
         if (!__BROWSER__) {
           // scope management
-          const { addIdentifiers, removeIdentifiers } = context
-
           // inject identifiers to context
           value && addIdentifiers(value)
           key && addIdentifiers(key)
           index && addIdentifiers(index)
+        }
+
+        return () => {
+          const params: ExpressionNode[] = []
+          if (value) {
+            params.push(value)
+          }
+          if (key) {
+            if (!value) {
+              params.push(createSimpleExpression(`_`, false))
+            }
+            params.push(key)
+          }
+          if (index) {
+            if (!key) {
+              params.push(createSimpleExpression(`__`, false))
+            }
+            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 */
+                )
+              ])
+            ])
+          )
 
-          return () => {
+          if (!__BROWSER__) {
             value && removeIdentifiers(value)
             key && removeIdentifiers(key)
             index && removeIdentifiers(index)
index 456cda0dadb17ee7350ed16c2de0e02523164f41..43aac0bcea075c86290076f8bf5ae65d93a01710 100644 (file)
@@ -20,7 +20,9 @@ import {
   ObjectExpression,
   createObjectProperty,
   Property,
-  ExpressionNode
+  ExpressionNode,
+  TemplateChildNode,
+  FunctionExpression
 } from '../ast'
 import { createCompilerError, ErrorCodes } from '../errors'
 import { processExpression } from './transformExpression'
@@ -68,7 +70,7 @@ export const transformIf = createStructuralDirectiveTransform(
       // transformed.
       return () => {
         codegenNode.expressions.push(
-          createCodegenNodeForBranch(node, branch, 0, context)
+          createCodegenNodeForBranch(branch, 0, context)
         )
       }
     } else {
@@ -105,7 +107,6 @@ export const transformIf = createStructuralDirectiveTransform(
               parentCondition = parentCondition.alternate
             } else {
               parentCondition.alternate = createCodegenNodeForBranch(
-                node,
                 branch,
                 sibling.branches.length - 1,
                 context
@@ -139,7 +140,6 @@ function createIfBranch(node: ElementNode, dir: DirectiveNode): IfBranchNode {
 }
 
 function createCodegenNodeForBranch(
-  node: ElementNode,
   branch: IfBranchNode,
   index: number,
   context: TransformContext
@@ -147,41 +147,50 @@ function createCodegenNodeForBranch(
   if (branch.condition) {
     return createConditionalExpression(
       branch.condition,
-      createChildrenCodegenNode(node, branch, index, context),
+      createChildrenCodegenNode(branch, index, context),
       createCallExpression(context.helper(CREATE_BLOCK), [
         context.helper(EMPTY)
       ])
     )
   } else {
-    return createChildrenCodegenNode(node, branch, index, context)
+    return createChildrenCodegenNode(branch, index, context)
   }
 }
 
 function createChildrenCodegenNode(
-  node: ElementNode,
   branch: IfBranchNode,
   index: number,
   { helper }: TransformContext
 ): CallExpression {
-  const isTemplate = node.tagType === ElementTypes.TEMPLATE
   const keyExp = `{ key: ${index} }`
-  if (isTemplate) {
+  const { children } = branch
+  const child = children[0]
+  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), [
       helper(FRAGMENT),
       keyExp,
-      branch.children
+      fragmentChildren
     ])
   } else {
-    let childCodegen = node.codegenNode!
-    if (childCodegen.callee.includes(APPLY_DIRECTIVES)) {
-      childCodegen = childCodegen.arguments[0] as CallExpression
+    const childCodegen = (child as ElementNode).codegenNode!
+    let vnodeCall = childCodegen
+    if (vnodeCall.callee.includes(APPLY_DIRECTIVES)) {
+      vnodeCall = vnodeCall.arguments[0] as CallExpression
     }
     // change child to a block
-    childCodegen.callee = helper(CREATE_BLOCK)
+    vnodeCall.callee = helper(CREATE_BLOCK)
     // branch key
-    const existingProps = childCodegen.arguments[1]
+    const existingProps = vnodeCall.arguments[1]
     if (!existingProps || existingProps === `null`) {
-      childCodegen.arguments[1] = keyExp
+      vnodeCall.arguments[1] = keyExp
     } else {
       // inject branch key if not already have a key
       const props = existingProps as
@@ -202,13 +211,13 @@ function createChildrenCodegenNode(
         props.properties.unshift(createKeyProperty(index))
       } else {
         // single v-bind with expression
-        childCodegen.arguments[1] = createCallExpression(helper(MERGE_PROPS), [
+        vnodeCall.arguments[1] = createCallExpression(helper(MERGE_PROPS), [
           keyExp,
           props
         ])
       }
     }
-    return node.codegenNode!
+    return childCodegen
   }
 }
 
index 7a1a0f7d33c86d87cee8507457520042c4f75664..45f7f00ee0429705a3f456b3b066c540b5dbd24b 100644 (file)
@@ -143,6 +143,7 @@ function buildSlot(
     createFunctionExpression(
       slotProps,
       children,
+      false,
       children.length ? children[0].loc : loc
     )
   )