Skip to content

Commit

Permalink
perf(compiler-core): treat v-for with constant exp as a stable fragme…
Browse files Browse the repository at this point in the history
…nt (#1394)
  • Loading branch information
HcySunYang authored Jun 17, 2020
1 parent 8899a90 commit 8a2cf21
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ return function render(_ctx, _cache) {
}"
`;

exports[`compiler: codegen forNode with constant expression 1`] = `
"
return function render(_ctx, _cache) {
with (_ctx) {
return (_openBlock(), _createBlock(_Fragment, null, _renderList(), 64 /* STABLE_FRAGMENT */))
}
}"
`;

exports[`compiler: codegen function mode preamble 1`] = `
"const _Vue = Vue
Expand Down
35 changes: 33 additions & 2 deletions packages/compiler-core/__tests__/codegen.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
FRAGMENT,
RENDER_LIST
} from '../src/runtimeHelpers'
import { createElementWithCodegen } from './testUtils'
import { createElementWithCodegen, genFlagText } from './testUtils'
import { PatchFlags } from '@vue/shared'

function createRoot(options: Partial<RootNode> = {}): RootNode {
Expand Down Expand Up @@ -283,7 +283,7 @@ describe('compiler: codegen', () => {
type: NodeTypes.VNODE_CALL,
tag: FRAGMENT,
isBlock: true,
isForBlock: true,
disableTracking: true,
props: undefined,
children: createCallExpression(RENDER_LIST),
patchFlag: '1',
Expand All @@ -298,6 +298,37 @@ describe('compiler: codegen', () => {
expect(code).toMatchSnapshot()
})

test('forNode with constant expression', () => {
const { code } = generate(
createRoot({
codegenNode: {
type: NodeTypes.FOR,
loc: locStub,
source: createSimpleExpression('1 + 2', false, locStub, true),
valueAlias: undefined,
keyAlias: undefined,
objectIndexAlias: undefined,
children: [],
parseResult: {} as any,
codegenNode: {
type: NodeTypes.VNODE_CALL,
tag: FRAGMENT,
isBlock: true,
disableTracking: false,
props: undefined,
children: createCallExpression(RENDER_LIST),
patchFlag: genFlagText(PatchFlags.STABLE_FRAGMENT),
dynamicProps: undefined,
directives: undefined,
loc: locStub
} as ForCodegenNode
}
})
)
expect(code).toMatch(`openBlock()`)
expect(code).toMatchSnapshot()
})

test('Element (callExpression + objectExpression + TemplateChildNode[])', () => {
const { code } = generate(
createRoot({
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler-core/__tests__/testUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function createElementWithCodegen(
dynamicProps,
directives: undefined,
isBlock: false,
isForBlock: false,
disableTracking: false,
loc: locStub
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,20 @@ return function render(_ctx, _cache) {
}"
`;

exports[`compiler: v-for codegen v-for with constant expression 1`] = `
"const _Vue = Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, toDisplayString: _toDisplayString, createVNode: _createVNode } = _Vue
return (_openBlock(), _createBlock(_Fragment, null, _renderList(10, (item) => {
return _createVNode(\\"p\\", null, _toDisplayString(item), 1 /* TEXT */)
}), 64 /* STABLE_FRAGMENT */))
}
}"
`;

exports[`compiler: v-for codegen v-if + v-for 1`] = `
"const _Vue = Vue
Expand Down
54 changes: 47 additions & 7 deletions packages/compiler-core/__tests__/transforms/vFor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -560,15 +560,18 @@ describe('compiler: v-for', () => {
function assertSharedCodegen(
node: ForCodegenNode,
keyed: boolean = false,
customReturn: boolean = false
customReturn: boolean = false,
disableTracking: boolean = true
) {
expect(node).toMatchObject({
type: NodeTypes.VNODE_CALL,
tag: FRAGMENT,
isForBlock: true,
patchFlag: keyed
? genFlagText(PatchFlags.KEYED_FRAGMENT)
: genFlagText(PatchFlags.UNKEYED_FRAGMENT),
disableTracking,
patchFlag: !disableTracking
? genFlagText(PatchFlags.STABLE_FRAGMENT)
: keyed
? genFlagText(PatchFlags.KEYED_FRAGMENT)
: genFlagText(PatchFlags.UNKEYED_FRAGMENT),
children: {
type: NodeTypes.JS_CALL_EXPRESSION,
callee: RENDER_LIST,
Expand All @@ -580,7 +583,7 @@ describe('compiler: v-for', () => {
? {}
: {
type: NodeTypes.VNODE_CALL,
isBlock: true
isBlock: disableTracking
}
}
]
Expand Down Expand Up @@ -658,6 +661,43 @@ describe('compiler: v-for', () => {
expect(generate(root).code).toMatchSnapshot()
})

test('v-for with constant expression', () => {
const {
root,
node: { codegenNode }
} = parseWithForTransform('<p v-for="item in 10">{{item}}</p>', {
prefixIdentifiers: true
})

expect(
assertSharedCodegen(
codegenNode,
false /* keyed */,
false /* customReturn */,
false /* disableTracking */
)
).toMatchObject({
source: { content: `10`, isConstant: true },
params: [{ content: `item` }],
innerVNodeCall: {
tag: `"p"`,
props: undefined,
isBlock: false,
children: {
type: NodeTypes.INTERPOLATION,
content: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'item',
isStatic: false,
isConstant: false
}
},
patchFlag: genFlagText(PatchFlags.TEXT)
}
})
expect(generate(root).code).toMatchSnapshot()
})

test('template v-for', () => {
const {
root,
Expand Down Expand Up @@ -777,7 +817,7 @@ describe('compiler: v-for', () => {
key: `[0]`
}),
isBlock: true,
isForBlock: true,
disableTracking: true,
patchFlag: genFlagText(PatchFlags.UNKEYED_FRAGMENT),
children: {
type: NodeTypes.JS_CALL_EXPRESSION,
Expand Down
8 changes: 4 additions & 4 deletions packages/compiler-core/src/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ export interface VNodeCall extends Node {
dynamicProps: string | undefined
directives: DirectiveArguments | undefined
isBlock: boolean
isForBlock: boolean
disableTracking: boolean
}

// JS Node Types ---------------------------------------------------------------
Expand Down Expand Up @@ -492,7 +492,7 @@ export interface ForCodegenNode extends VNodeCall {
props: undefined
children: ForRenderListExpression
patchFlag: string
isForBlock: true
disableTracking: boolean
}

export interface ForRenderListExpression extends CallExpression {
Expand Down Expand Up @@ -543,7 +543,7 @@ export function createVNodeCall(
dynamicProps?: VNodeCall['dynamicProps'],
directives?: VNodeCall['directives'],
isBlock: VNodeCall['isBlock'] = false,
isForBlock: VNodeCall['isForBlock'] = false,
disableTracking: VNodeCall['disableTracking'] = false,
loc = locStub
): VNodeCall {
if (context) {
Expand All @@ -567,7 +567,7 @@ export function createVNodeCall(
dynamicProps,
directives,
isBlock,
isForBlock,
disableTracking,
loc
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/compiler-core/src/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -698,13 +698,13 @@ function genVNodeCall(node: VNodeCall, context: CodegenContext) {
dynamicProps,
directives,
isBlock,
isForBlock
disableTracking
} = node
if (directives) {
push(helper(WITH_DIRECTIVES) + `(`)
}
if (isBlock) {
push(`(${helper(OPEN_BLOCK)}(${isForBlock ? `true` : ``}), `)
push(`(${helper(OPEN_BLOCK)}(${disableTracking ? `true` : ``}), `)
}
if (pure) {
push(PURE_ANNOTATION)
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler-core/src/transforms/transformElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export const transformElement: NodeTransform = (node, context) => {
vnodeDynamicProps,
vnodeDirectives,
!!shouldUseBlock,
false /* isForBlock */,
false /* disableTracking */,
node.loc
)
}
Expand Down
21 changes: 14 additions & 7 deletions packages/compiler-core/src/transforms/vFor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,14 @@ export const transformFor = createStructuralDirectiveTransform(
forNode.source
]) as ForRenderListExpression
const keyProp = findProp(node, `key`)
const fragmentFlag = keyProp
? PatchFlags.KEYED_FRAGMENT
: PatchFlags.UNKEYED_FRAGMENT
const isStableFragment =
forNode.source.type === NodeTypes.SIMPLE_EXPRESSION &&
forNode.source.isConstant
const fragmentFlag = isStableFragment
? PatchFlags.STABLE_FRAGMENT
: keyProp
? PatchFlags.KEYED_FRAGMENT
: PatchFlags.UNKEYED_FRAGMENT
forNode.codegenNode = createVNodeCall(
context,
helper(FRAGMENT),
Expand All @@ -67,7 +72,7 @@ export const transformFor = createStructuralDirectiveTransform(
undefined,
undefined,
true /* isBlock */,
true /* isForBlock */,
!isStableFragment /* disableTracking */,
node.loc
) as ForCodegenNode

Expand Down Expand Up @@ -122,9 +127,11 @@ export const transformFor = createStructuralDirectiveTransform(
// but mark it as a block.
childBlock = (children[0] as PlainElementNode)
.codegenNode as VNodeCall
childBlock.isBlock = true
helper(OPEN_BLOCK)
helper(CREATE_BLOCK)
childBlock.isBlock = !isStableFragment
if (childBlock.isBlock) {
helper(OPEN_BLOCK)
helper(CREATE_BLOCK)
}
}

renderExp.arguments.push(createFunctionExpression(
Expand Down

0 comments on commit 8a2cf21

Please sign in to comment.