fix(compiler): v-for fragments should be blocks

This commit is contained in:
Evan You 2019-10-02 10:47:01 -04:00
parent 191db785bd
commit bec01c93bd
6 changed files with 96 additions and 80 deletions

View File

@ -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 */)
}"
`;

View File

@ -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/ <slot/> 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 */))
}
}"
`;

View File

@ -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,10 +565,20 @@ 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_SEQUENCE_EXPRESSION,
expressions: [
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: `_${CREATE_VNODE}`,
callee: `_${OPEN_BLOCK}`
},
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: `_${CREATE_BLOCK}`,
arguments: [
`_${FRAGMENT}`,
`null`,
@ -603,8 +613,11 @@ describe('compiler: v-for', () => {
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,

View File

@ -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.

View File

@ -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,13 +51,16 @@ export const transformFor = createStructuralDirectiveTransform(
const fragmentFlag = keyProp
? PatchFlags.KEYED_FRAGMENT
: PatchFlags.UNKEYED_FRAGMENT
const codegenNode = createCallExpression(helper(CREATE_VNODE), [
const codegenNode = createSequenceExpression([
createCallExpression(helper(OPEN_BLOCK)),
createCallExpression(helper(CREATE_BLOCK), [
helper(FRAGMENT),
`null`,
renderExp,
fragmentFlag +
(__DEV__ ? ` /* ${PatchFlagNames[fragmentFlag]} */` : ``)
])
])
context.replaceNode({
type: NodeTypes.FOR,

View File

@ -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]