From bec01c93bdb7c5a427b88cd58df2deb09eae5f72 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 2 Oct 2019 10:47:01 -0400 Subject: [PATCH] fix(compiler): v-for fragments should be blocks --- .../__snapshots__/compile.spec.ts.snap | 12 +-- .../__snapshots__/vFor.spec.ts.snap | 56 ++++++------ .../__tests__/transforms/vFor.spec.ts | 85 +++++++++++-------- packages/compiler-core/src/ast.ts | 2 +- packages/compiler-core/src/transforms/vFor.ts | 18 ++-- packages/compiler-core/src/transforms/vIf.ts | 3 +- 6 files changed, 96 insertions(+), 80 deletions(-) diff --git a/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap index 9bf7229e5..8d7ae5dd4 100644 --- a/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap @@ -17,11 +17,11 @@ return function render() { : _createBlock(_Fragment, { key: 1 }, [ \\"no\\" ])), - _createVNode(_Fragment, null, _renderList(list, (value, index) => { + (_openBlock(), _createBlock(_Fragment, null, _renderList(list, (value, index) => { return (_openBlock(), _createBlock(\\"div\\", null, [ _createVNode(\\"span\\", null, _toString(value + index), 1 /* TEXT */) ])) - }), 128 /* UNKEYED_FRAGMENT */) + }), 128 /* UNKEYED_FRAGMENT */)) ], 2 /* CLASS */) } }" @@ -42,11 +42,11 @@ return function render() { : createBlock(Fragment, { key: 1 }, [ \\"no\\" ])), - createVNode(Fragment, null, renderList(_ctx.list, (value, index) => { + (openBlock(), createBlock(Fragment, null, renderList(_ctx.list, (value, index) => { return (openBlock(), createBlock(\\"div\\", null, [ createVNode(\\"span\\", null, toString(value + index), 1 /* TEXT */) ])) - }), 128 /* UNKEYED_FRAGMENT */) + }), 128 /* UNKEYED_FRAGMENT */)) ], 2 /* CLASS */) }" `; @@ -66,11 +66,11 @@ export default function render() { : createBlock(Fragment, { key: 1 }, [ \\"no\\" ])), - createVNode(Fragment, null, renderList(_ctx.list, (value, index) => { + (openBlock(), createBlock(Fragment, null, renderList(_ctx.list, (value, index) => { return (openBlock(), createBlock(\\"div\\", null, [ createVNode(\\"span\\", null, _toString(value + index), 1 /* TEXT */) ])) - }), 128 /* UNKEYED_FRAGMENT */) + }), 128 /* UNKEYED_FRAGMENT */)) ], 2 /* CLASS */) }" `; diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vFor.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vFor.spec.ts.snap index 29a751517..c2df6a120 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/vFor.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vFor.spec.ts.snap @@ -5,11 +5,11 @@ exports[`compiler: v-for codegen basic v-for 1`] = ` return function render() { with (this) { - const { renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, createBlock: _createBlock, openBlock: _openBlock } = _Vue + const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode } = _Vue - return _createVNode(_Fragment, null, _renderList(items, (item) => { + return (_openBlock(), _createBlock(_Fragment, null, _renderList(items, (item) => { return (_openBlock(), _createBlock(\\"span\\")) - }), 128 /* UNKEYED_FRAGMENT */) + }), 128 /* UNKEYED_FRAGMENT */)) } }" `; @@ -19,14 +19,14 @@ exports[`compiler: v-for codegen keyed template v-for 1`] = ` return function render() { with (this) { - const { renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode } = _Vue - return _createVNode(_Fragment, null, _renderList(items, (item) => { + return (_openBlock(), _createBlock(_Fragment, null, _renderList(items, (item) => { return (_openBlock(), _createBlock(_Fragment, { key: item }, [ \\"hello\\", _createVNode(\\"span\\") ])) - }), 64 /* KEYED_FRAGMENT */) + }), 64 /* KEYED_FRAGMENT */)) } }" `; @@ -36,11 +36,11 @@ exports[`compiler: v-for codegen keyed v-for 1`] = ` return function render() { with (this) { - const { renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, createBlock: _createBlock, openBlock: _openBlock } = _Vue + const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode } = _Vue - return _createVNode(_Fragment, null, _renderList(items, (item) => { + return (_openBlock(), _createBlock(_Fragment, null, _renderList(items, (item) => { return (_openBlock(), _createBlock(\\"span\\", { key: item })) - }), 64 /* KEYED_FRAGMENT */) + }), 64 /* KEYED_FRAGMENT */)) } }" `; @@ -50,11 +50,11 @@ exports[`compiler: v-for codegen skipped key 1`] = ` return function render() { with (this) { - const { renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, createBlock: _createBlock, openBlock: _openBlock } = _Vue + const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode } = _Vue - return _createVNode(_Fragment, null, _renderList(items, (item, __, index) => { + return (_openBlock(), _createBlock(_Fragment, null, _renderList(items, (item, __, index) => { return (_openBlock(), _createBlock(\\"span\\")) - }), 128 /* UNKEYED_FRAGMENT */) + }), 128 /* UNKEYED_FRAGMENT */)) } }" `; @@ -64,11 +64,11 @@ exports[`compiler: v-for codegen skipped value & key 1`] = ` return function render() { with (this) { - const { renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, createBlock: _createBlock, openBlock: _openBlock } = _Vue + const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode } = _Vue - return _createVNode(_Fragment, null, _renderList(items, (_, __, index) => { + return (_openBlock(), _createBlock(_Fragment, null, _renderList(items, (_, __, index) => { return (_openBlock(), _createBlock(\\"span\\")) - }), 128 /* UNKEYED_FRAGMENT */) + }), 128 /* UNKEYED_FRAGMENT */)) } }" `; @@ -78,11 +78,11 @@ exports[`compiler: v-for codegen skipped value 1`] = ` return function render() { with (this) { - const { renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, createBlock: _createBlock, openBlock: _openBlock } = _Vue + const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode } = _Vue - return _createVNode(_Fragment, null, _renderList(items, (_, key, index) => { + return (_openBlock(), _createBlock(_Fragment, null, _renderList(items, (_, key, index) => { return (_openBlock(), _createBlock(\\"span\\")) - }), 128 /* UNKEYED_FRAGMENT */) + }), 128 /* UNKEYED_FRAGMENT */)) } }" `; @@ -92,14 +92,14 @@ exports[`compiler: v-for codegen template v-for 1`] = ` return function render() { with (this) { - const { renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode } = _Vue - return _createVNode(_Fragment, null, _renderList(items, (item) => { + return (_openBlock(), _createBlock(_Fragment, null, _renderList(items, (item) => { return (_openBlock(), _createBlock(_Fragment, null, [ \\"hello\\", _createVNode(\\"span\\") ])) - }), 128 /* UNKEYED_FRAGMENT */) + }), 128 /* UNKEYED_FRAGMENT */)) } }" `; @@ -109,11 +109,11 @@ exports[`compiler: v-for codegen template v-for w/ 1`] = ` return function render() { with (this) { - const { renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, renderSlot: _renderSlot, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, renderSlot: _renderSlot } = _Vue - return _createVNode(_Fragment, null, _renderList(items, (item) => { + return (_openBlock(), _createBlock(_Fragment, null, _renderList(items, (item) => { return (_openBlock(), _createBlock(_Fragment, null, _renderSlot($slots.default))) - }), 128 /* UNKEYED_FRAGMENT */) + }), 128 /* UNKEYED_FRAGMENT */)) } }" `; @@ -123,7 +123,7 @@ exports[`compiler: v-for codegen v-if + v-for 1`] = ` return function render() { with (this) { - const { openBlock: _openBlock, renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, createBlock: _createBlock, Empty: _Empty } = _Vue + const { openBlock: _openBlock, renderList: _renderList, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode, Empty: _Empty } = _Vue return (_openBlock(), ok ? _createBlock(_Fragment, { key: 0 }, _renderList(list, (i) => { @@ -139,11 +139,11 @@ exports[`compiler: v-for codegen value + key + index 1`] = ` return function render() { with (this) { - const { renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, createBlock: _createBlock, openBlock: _openBlock } = _Vue + const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode } = _Vue - return _createVNode(_Fragment, null, _renderList(items, (item, key, index) => { + return (_openBlock(), _createBlock(_Fragment, null, _renderList(items, (item, key, index) => { return (_openBlock(), _createBlock(\\"span\\")) - }), 128 /* UNKEYED_FRAGMENT */) + }), 128 /* UNKEYED_FRAGMENT */)) } }" `; diff --git a/packages/compiler-core/__tests__/transforms/vFor.spec.ts b/packages/compiler-core/__tests__/transforms/vFor.spec.ts index 161a5e0b5..0357443c2 100644 --- a/packages/compiler-core/__tests__/transforms/vFor.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vFor.spec.ts @@ -12,7 +12,8 @@ import { SimpleExpressionNode, ElementNode, InterpolationNode, - CallExpression + CallExpression, + SequenceExpression } from '../../src/ast' import { ErrorCodes } from '../../src/errors' import { CompilerOptions, generate } from '../../src' @@ -21,7 +22,6 @@ import { CREATE_BLOCK, FRAGMENT, RENDER_LIST, - CREATE_VNODE, RENDER_SLOT } from '../../src/runtimeConstants' import { PatchFlags } from '@vue/runtime-dom' @@ -565,46 +565,59 @@ describe('compiler: v-for', () => { }) describe('codegen', () => { - function assertSharedCodegen(node: CallExpression, keyed: boolean = false) { + function assertSharedCodegen( + node: SequenceExpression, + keyed: boolean = false + ) { expect(node).toMatchObject({ - type: NodeTypes.JS_CALL_EXPRESSION, - callee: `_${CREATE_VNODE}`, - arguments: [ - `_${FRAGMENT}`, - `null`, + type: NodeTypes.JS_SEQUENCE_EXPRESSION, + expressions: [ { type: NodeTypes.JS_CALL_EXPRESSION, - callee: `_${RENDER_LIST}`, - arguments: [ - {}, // to be asserted by each test - { - type: NodeTypes.JS_FUNCTION_EXPRESSION, - returns: { - type: NodeTypes.JS_SEQUENCE_EXPRESSION, - expressions: [ - { - type: NodeTypes.JS_CALL_EXPRESSION, - callee: `_${OPEN_BLOCK}` - }, - { - type: NodeTypes.JS_CALL_EXPRESSION, - callee: `_${CREATE_BLOCK}` - } - ] - } - } - ] + callee: `_${OPEN_BLOCK}` }, - keyed - ? `${PatchFlags.KEYED_FRAGMENT} /* ${ - PatchFlagNames[PatchFlags.KEYED_FRAGMENT] - } */` - : `${PatchFlags.UNKEYED_FRAGMENT} /* ${ - PatchFlagNames[PatchFlags.UNKEYED_FRAGMENT] - } */` + { + type: NodeTypes.JS_CALL_EXPRESSION, + callee: `_${CREATE_BLOCK}`, + arguments: [ + `_${FRAGMENT}`, + `null`, + { + type: NodeTypes.JS_CALL_EXPRESSION, + callee: `_${RENDER_LIST}`, + arguments: [ + {}, // to be asserted by each test + { + type: NodeTypes.JS_FUNCTION_EXPRESSION, + returns: { + type: NodeTypes.JS_SEQUENCE_EXPRESSION, + expressions: [ + { + type: NodeTypes.JS_CALL_EXPRESSION, + callee: `_${OPEN_BLOCK}` + }, + { + type: NodeTypes.JS_CALL_EXPRESSION, + callee: `_${CREATE_BLOCK}` + } + ] + } + } + ] + }, + keyed + ? `${PatchFlags.KEYED_FRAGMENT} /* ${ + PatchFlagNames[PatchFlags.KEYED_FRAGMENT] + } */` + : `${PatchFlags.UNKEYED_FRAGMENT} /* ${ + PatchFlagNames[PatchFlags.UNKEYED_FRAGMENT] + } */` + ] + } ] }) - const renderListArgs = (node.arguments[2] as CallExpression).arguments + const renderListArgs = ((node.expressions[1] as CallExpression) + .arguments[2] as CallExpression).arguments return { source: renderListArgs[0] as SimpleExpressionNode, params: (renderListArgs[1] as any).params, diff --git a/packages/compiler-core/src/ast.ts b/packages/compiler-core/src/ast.ts index c0941afc3..52cda7aea 100644 --- a/packages/compiler-core/src/ast.ts +++ b/packages/compiler-core/src/ast.ts @@ -158,7 +158,7 @@ export interface ForNode extends Node { keyAlias: ExpressionNode | undefined objectIndexAlias: ExpressionNode | undefined children: TemplateChildNode[] - codegenNode: CallExpression + codegenNode: SequenceExpression } // We also include a number of JavaScript AST nodes for code generation. diff --git a/packages/compiler-core/src/transforms/vFor.ts b/packages/compiler-core/src/transforms/vFor.ts index e73e280ee..1abc4994e 100644 --- a/packages/compiler-core/src/transforms/vFor.ts +++ b/packages/compiler-core/src/transforms/vFor.ts @@ -24,8 +24,7 @@ import { RENDER_LIST, OPEN_BLOCK, CREATE_BLOCK, - FRAGMENT, - CREATE_VNODE + FRAGMENT } from '../runtimeConstants' import { processExpression } from './transformExpression' import { PatchFlags, PatchFlagNames } from '@vue/shared' @@ -52,12 +51,15 @@ export const transformFor = createStructuralDirectiveTransform( const fragmentFlag = keyProp ? PatchFlags.KEYED_FRAGMENT : PatchFlags.UNKEYED_FRAGMENT - const codegenNode = createCallExpression(helper(CREATE_VNODE), [ - helper(FRAGMENT), - `null`, - renderExp, - fragmentFlag + - (__DEV__ ? ` /* ${PatchFlagNames[fragmentFlag]} */` : ``) + const codegenNode = createSequenceExpression([ + createCallExpression(helper(OPEN_BLOCK)), + createCallExpression(helper(CREATE_BLOCK), [ + helper(FRAGMENT), + `null`, + renderExp, + fragmentFlag + + (__DEV__ ? ` /* ${PatchFlagNames[fragmentFlag]} */` : ``) + ]) ]) context.replaceNode({ diff --git a/packages/compiler-core/src/transforms/vIf.ts b/packages/compiler-core/src/transforms/vIf.ts index 404662702..24760e472 100644 --- a/packages/compiler-core/src/transforms/vIf.ts +++ b/packages/compiler-core/src/transforms/vIf.ts @@ -176,7 +176,8 @@ function createChildrenCodegenNode( if (children.length === 1) { // optimize away nested fragments when child is a ForNode if (child.type === NodeTypes.FOR) { - const forBlockArgs = child.codegenNode.arguments + const forBlockArgs = (child.codegenNode + .expressions[1] as CallExpression).arguments // directly use the for block's children and patchFlag blockArgs[2] = forBlockArgs[2] blockArgs[3] = forBlockArgs[3]