wip(compiler): codegen node w/ block optimization for v-for

This commit is contained in:
Evan You 2019-10-01 16:48:20 -04:00
parent aa134e7a4f
commit e5bc17967d
9 changed files with 353 additions and 409 deletions

View 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

View 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)
}"
`;

View 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(

View 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` }, ` }`]

View 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,

View 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(

View 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 */
)
])
])
)
if (!__BROWSER__) {
value && removeIdentifiers(value)
key && removeIdentifiers(key)
index && removeIdentifiers(index)

View 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
}
}

View File

@ -143,6 +143,7 @@ function buildSlot(
createFunctionExpression(
slotProps,
children,
false,
children.length ? children[0].loc : loc
)
)