feat(compiler): v-for codegen w/ correct blocks optimization + key flags

This commit is contained in:
Evan You
2019-10-01 23:19:48 -04:00
parent 07955e6c96
commit a477594d65
12 changed files with 324 additions and 173 deletions

View File

@@ -15,11 +15,11 @@ return function render() {
(_openBlock(), ok
? _createBlock(\\"div\\", { key: 0 }, \\"yes\\")
: _createBlock(_Fragment, { key: 1 }, \\"no\\")),
(_openBlock(), _createBlock(_Fragment, null, _renderList(list, (value, index) => {
return _createVNode(\\"div\\", null, [
_createVNode(_Fragment, null, _renderList(list, (value, index) => {
return (_openBlock(), _createBlock(\\"div\\", null, [
_createVNode(\\"span\\", null, _toString(value + index))
])
})))
]))
}), 128 /* UNKEYED_FRAGMENT */)
], 2 /* CLASS */)
}
}"
@@ -38,11 +38,11 @@ return function render() {
(openBlock(), (_ctx.ok)
? createBlock(\\"div\\", { key: 0 }, \\"yes\\")
: createBlock(Fragment, { key: 1 }, \\"no\\")),
(openBlock(), createBlock(Fragment, null, renderList(_ctx.list, (value, index) => {
return createVNode(\\"div\\", null, [
createVNode(Fragment, null, renderList(_ctx.list, (value, index) => {
return (openBlock(), createBlock(\\"div\\", null, [
createVNode(\\"span\\", null, toString(value + index))
])
})))
]))
}), 128 /* UNKEYED_FRAGMENT */)
], 2 /* CLASS */)
}"
`;
@@ -60,11 +60,11 @@ export default function render() {
(openBlock(), (_ctx.ok)
? createBlock(\\"div\\", { key: 0 }, \\"yes\\")
: createBlock(Fragment, { key: 1 }, \\"no\\")),
(openBlock(), createBlock(Fragment, null, renderList(_ctx.list, (value, index) => {
return createVNode(\\"div\\", null, [
createVNode(Fragment, null, renderList(_ctx.list, (value, index) => {
return (openBlock(), createBlock(\\"div\\", null, [
createVNode(\\"span\\", null, _toString(value + index))
])
})))
]))
}), 128 /* UNKEYED_FRAGMENT */)
], 2 /* CLASS */)
}"
`;

View File

@@ -5,11 +5,42 @@ exports[`compiler: v-for codegen basic v-for 1`] = `
return function render() {
with (this) {
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, Fragment: _Fragment, renderList: _renderList } = _Vue
const { renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return (_openBlock(), _createBlock(_Fragment, null, _renderList(items, (item) => {
return _createVNode(\\"span\\")
})))
return _createVNode(_Fragment, null, _renderList(items, (item) => {
return (_openBlock(), _createBlock(\\"span\\"))
}), 128 /* UNKEYED_FRAGMENT */)
}
}"
`;
exports[`compiler: v-for codegen keyed template v-for 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return _createVNode(_Fragment, null, _renderList(items, (item) => {
return (_openBlock(), _createBlock(_Fragment, { key: item }, [
\\"hello\\",
_createVNode(\\"span\\")
]))
}), 64 /* KEYED_FRAGMENT */)
}
}"
`;
exports[`compiler: v-for codegen keyed v-for 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return _createVNode(_Fragment, null, _renderList(items, (item) => {
return (_openBlock(), _createBlock(\\"span\\", { key: item }))
}), 64 /* KEYED_FRAGMENT */)
}
}"
`;
@@ -19,11 +50,11 @@ exports[`compiler: v-for codegen skipped key 1`] = `
return function render() {
with (this) {
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, Fragment: _Fragment, renderList: _renderList } = _Vue
const { renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return (_openBlock(), _createBlock(_Fragment, null, _renderList(items, (value, __, index) => {
return _createVNode(\\"span\\")
})))
return _createVNode(_Fragment, null, _renderList(items, (item, __, index) => {
return (_openBlock(), _createBlock(\\"span\\"))
}), 128 /* UNKEYED_FRAGMENT */)
}
}"
`;
@@ -33,11 +64,11 @@ exports[`compiler: v-for codegen skipped value & key 1`] = `
return function render() {
with (this) {
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, Fragment: _Fragment, renderList: _renderList } = _Vue
const { renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return (_openBlock(), _createBlock(_Fragment, null, _renderList(items, (_, __, index) => {
return _createVNode(\\"span\\")
})))
return _createVNode(_Fragment, null, _renderList(items, (_, __, index) => {
return (_openBlock(), _createBlock(\\"span\\"))
}), 128 /* UNKEYED_FRAGMENT */)
}
}"
`;
@@ -47,11 +78,11 @@ exports[`compiler: v-for codegen skipped value 1`] = `
return function render() {
with (this) {
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, Fragment: _Fragment, renderList: _renderList } = _Vue
const { renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return (_openBlock(), _createBlock(_Fragment, null, _renderList(items, (_, key, index) => {
return _createVNode(\\"span\\")
})))
return _createVNode(_Fragment, null, _renderList(items, (_, key, index) => {
return (_openBlock(), _createBlock(\\"span\\"))
}), 128 /* UNKEYED_FRAGMENT */)
}
}"
`;
@@ -61,14 +92,14 @@ exports[`compiler: v-for codegen template v-for 1`] = `
return function render() {
with (this) {
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, Fragment: _Fragment, renderList: _renderList } = _Vue
const { renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return (_openBlock(), _createBlock(_Fragment, null, _renderList(items, (item) => {
return [
return _createVNode(_Fragment, null, _renderList(items, (item) => {
return (_openBlock(), _createBlock(_Fragment, null, [
\\"hello\\",
_createVNode(\\"span\\")
]
})))
]))
}), 128 /* UNKEYED_FRAGMENT */)
}
}"
`;
@@ -78,12 +109,12 @@ exports[`compiler: v-for codegen v-if + v-for 1`] = `
return function render() {
with (this) {
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, Fragment: _Fragment, renderList: _renderList, Empty: _Empty } = _Vue
const { openBlock: _openBlock, renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, createBlock: _createBlock, Empty: _Empty } = _Vue
return (_openBlock(), ok
? _createBlock(_Fragment, { key: 0 }, _renderList(list, (i) => {
return _createVNode(\\"div\\")
}))
return (_openBlock(), _createBlock(\\"div\\"))
}), 128 /* UNKEYED_FRAGMENT */)
: _createBlock(_Empty))
}
}"
@@ -94,11 +125,11 @@ exports[`compiler: v-for codegen value + key + index 1`] = `
return function render() {
with (this) {
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, Fragment: _Fragment, renderList: _renderList } = _Vue
const { renderList: _renderList, createVNode: _createVNode, Fragment: _Fragment, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return (_openBlock(), _createBlock(_Fragment, null, _renderList(items, (item, key, index) => {
return _createVNode(\\"span\\")
})))
return _createVNode(_Fragment, null, _renderList(items, (item, key, index) => {
return (_openBlock(), _createBlock(\\"span\\"))
}), 128 /* UNKEYED_FRAGMENT */)
}
}"
`;

View File

@@ -19,7 +19,7 @@ exports[`compiler: v-if codegen template v-if 1`] = `
return function render() {
with (this) {
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, Fragment: _Fragment, Empty: _Empty } = _Vue
const { openBlock: _openBlock, createVNode: _createVNode, Fragment: _Fragment, createBlock: _createBlock, Empty: _Empty } = _Vue
return (_openBlock(), ok
? _createBlock(_Fragment, { key: 0 }, [

View File

@@ -2,25 +2,29 @@ import { parse } from '../../src/parse'
import { transform } from '../../src/transform'
import { transformIf } from '../../src/transforms/vIf'
import { transformFor } from '../../src/transforms/vFor'
import { transformBind } from '../../src/transforms/vBind'
import { transformElement } from '../../src/transforms/transformElement'
import { transformExpression } from '../../src/transforms/transformExpression'
import {
ForNode,
NodeTypes,
SimpleExpressionNode,
ElementNode,
InterpolationNode,
SequenceExpression,
CallExpression
} from '../../src/ast'
import { ErrorCodes } from '../../src/errors'
import { CompilerOptions, generate } from '../../src'
import { transformExpression } from '../../src/transforms/transformExpression'
import {
OPEN_BLOCK,
CREATE_BLOCK,
FRAGMENT,
RENDER_LIST
RENDER_LIST,
CREATE_VNODE
} from '../../src/runtimeConstants'
import { PatchFlags } from '@vue/runtime-dom'
import { PatchFlagNames } from '@vue/shared'
import { createObjectMatcher } from '../testUtils'
function parseWithForTransform(
template: string,
@@ -34,6 +38,9 @@ function parseWithForTransform(
...(options.prefixIdentifiers ? [transformExpression] : []),
transformElement
],
directiveTransforms: {
bind: transformBind
},
...options
})
return {
@@ -555,31 +562,51 @@ describe('compiler: v-for', () => {
})
describe('codegen', () => {
function assertSharedCodegen(node: SequenceExpression) {
function assertSharedCodegen(node: CallExpression, keyed: boolean = false) {
expect(node).toMatchObject({
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
expressions: [
type: NodeTypes.JS_CALL_EXPRESSION,
callee: `_${CREATE_VNODE}`,
arguments: [
`_${FRAGMENT}`,
`null`,
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: `_${OPEN_BLOCK}`,
arguments: []
},
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: `_${CREATE_BLOCK}`,
callee: `_${RENDER_LIST}`,
arguments: [
`_${FRAGMENT}`,
`null`,
{}, // to be asserted by each test
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: `_${RENDER_LIST}`
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]
} */`
]
})
return (node.expressions[1] as CallExpression)
.arguments[2] as CallExpression
const renderListArgs = (node.arguments[2] as CallExpression).arguments
return {
source: renderListArgs[0] as SimpleExpressionNode,
params: (renderListArgs[1] as any).params,
blockArgs: (renderListArgs[1] as any).returns.expressions[1].arguments
}
}
test('basic v-for', () => {
@@ -587,17 +614,11 @@ describe('compiler: v-for', () => {
root,
node: { codegenNode }
} = parseWithForTransform('<span v-for="(item) in items" />')
expect(assertSharedCodegen(codegenNode).arguments).toMatchObject([
{ content: `items` },
{
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: [{ content: `item` }],
returns: {
type: NodeTypes.ELEMENT,
tag: `span`
}
}
])
expect(assertSharedCodegen(codegenNode)).toMatchObject({
source: { content: `items` },
params: [{ content: `item` }],
blockArgs: [`"span"`]
})
expect(generate(root).code).toMatchSnapshot()
})
@@ -606,17 +627,10 @@ describe('compiler: v-for', () => {
root,
node: { codegenNode }
} = parseWithForTransform('<span v-for="(item, key, index) in items" />')
expect(assertSharedCodegen(codegenNode).arguments).toMatchObject([
{ content: `items` },
{
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: [
{ content: `item` },
{ content: `key` },
{ content: `index` }
]
}
])
expect(assertSharedCodegen(codegenNode)).toMatchObject({
source: { content: `items` },
params: [{ content: `item` }, { content: `key` }, { content: `index` }]
})
expect(generate(root).code).toMatchSnapshot()
})
@@ -624,14 +638,11 @@ describe('compiler: v-for', () => {
const {
root,
node: { codegenNode }
} = parseWithForTransform('<span v-for="(,key,index) in items" />')
expect(assertSharedCodegen(codegenNode).arguments).toMatchObject([
{ content: `items` },
{
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: [{ content: `_` }, { content: `key` }, { content: `index` }]
}
])
} = parseWithForTransform('<span v-for="(, key, index) in items" />')
expect(assertSharedCodegen(codegenNode)).toMatchObject({
source: { content: `items` },
params: [{ content: `_` }, { content: `key` }, { content: `index` }]
})
expect(generate(root).code).toMatchSnapshot()
})
@@ -639,18 +650,11 @@ describe('compiler: v-for', () => {
const {
root,
node: { codegenNode }
} = parseWithForTransform('<span v-for="(value,,index) in items" />')
expect(assertSharedCodegen(codegenNode).arguments).toMatchObject([
{ content: `items` },
{
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: [
{ content: `value` },
{ content: `__` },
{ content: `index` }
]
}
])
} = parseWithForTransform('<span v-for="(item,,index) in items" />')
expect(assertSharedCodegen(codegenNode)).toMatchObject({
source: { content: `items` },
params: [{ content: `item` }, { content: `__` }, { content: `index` }]
})
expect(generate(root).code).toMatchSnapshot()
})
@@ -659,13 +663,10 @@ describe('compiler: v-for', () => {
root,
node: { codegenNode }
} = parseWithForTransform('<span v-for="(,,index) in items" />')
expect(assertSharedCodegen(codegenNode).arguments).toMatchObject([
{ content: `items` },
{
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: [{ content: `_` }, { content: `__` }, { content: `index` }]
}
])
expect(assertSharedCodegen(codegenNode)).toMatchObject({
source: { content: `items` },
params: [{ content: `_` }, { content: `__` }, { content: `index` }]
})
expect(generate(root).code).toMatchSnapshot()
})
@@ -676,17 +677,60 @@ describe('compiler: v-for', () => {
} = parseWithForTransform(
'<template v-for="item in items">hello<span/></template>'
)
expect(assertSharedCodegen(codegenNode).arguments).toMatchObject([
{ content: `items` },
{
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: [{ content: `item` }],
returns: [
expect(assertSharedCodegen(codegenNode)).toMatchObject({
source: { content: `items` },
params: [{ content: `item` }],
blockArgs: [
`_${FRAGMENT}`,
`null`,
[
{ type: NodeTypes.TEXT, content: `hello` },
{ type: NodeTypes.ELEMENT, tag: `span` }
]
}
])
]
})
expect(generate(root).code).toMatchSnapshot()
})
test('keyed v-for', () => {
const {
root,
node: { codegenNode }
} = parseWithForTransform('<span v-for="(item) in items" :key="item" />')
expect(assertSharedCodegen(codegenNode, true)).toMatchObject({
source: { content: `items` },
params: [{ content: `item` }],
blockArgs: [
`"span"`,
createObjectMatcher({
key: `[item]`
})
]
})
expect(generate(root).code).toMatchSnapshot()
})
test('keyed template v-for', () => {
const {
root,
node: { codegenNode }
} = parseWithForTransform(
'<template v-for="item in items" :key="item">hello<span/></template>'
)
expect(assertSharedCodegen(codegenNode, true)).toMatchObject({
source: { content: `items` },
params: [{ content: `item` }],
blockArgs: [
`_${FRAGMENT}`,
createObjectMatcher({
key: `[item]`
}),
[
{ type: NodeTypes.TEXT, content: `hello` },
{ type: NodeTypes.ELEMENT, tag: `span` }
]
]
})
expect(generate(root).code).toMatchSnapshot()
})
@@ -722,12 +766,25 @@ describe('compiler: v-for', () => {
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: [{ content: `i` }],
returns: {
type: NodeTypes.ELEMENT,
tag: `div`
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
expressions: [
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: `_${OPEN_BLOCK}`
},
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: `_${CREATE_BLOCK}`,
arguments: [`"div"`]
}
]
}
}
]
}
},
`${PatchFlags.UNKEYED_FRAGMENT} /* ${
PatchFlagNames[PatchFlags.UNKEYED_FRAGMENT]
} */`
]
}
}