perf(compiler-core): treat v-for with constant exp as a stable fragment (#1394)
This commit is contained in:
parent
8899a90fc4
commit
8a2cf21b71
@ -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`] = `
|
exports[`compiler: codegen function mode preamble 1`] = `
|
||||||
"const _Vue = Vue
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ import {
|
|||||||
FRAGMENT,
|
FRAGMENT,
|
||||||
RENDER_LIST
|
RENDER_LIST
|
||||||
} from '../src/runtimeHelpers'
|
} from '../src/runtimeHelpers'
|
||||||
import { createElementWithCodegen } from './testUtils'
|
import { createElementWithCodegen, genFlagText } from './testUtils'
|
||||||
import { PatchFlags } from '@vue/shared'
|
import { PatchFlags } from '@vue/shared'
|
||||||
|
|
||||||
function createRoot(options: Partial<RootNode> = {}): RootNode {
|
function createRoot(options: Partial<RootNode> = {}): RootNode {
|
||||||
@ -283,7 +283,7 @@ describe('compiler: codegen', () => {
|
|||||||
type: NodeTypes.VNODE_CALL,
|
type: NodeTypes.VNODE_CALL,
|
||||||
tag: FRAGMENT,
|
tag: FRAGMENT,
|
||||||
isBlock: true,
|
isBlock: true,
|
||||||
isForBlock: true,
|
disableTracking: true,
|
||||||
props: undefined,
|
props: undefined,
|
||||||
children: createCallExpression(RENDER_LIST),
|
children: createCallExpression(RENDER_LIST),
|
||||||
patchFlag: '1',
|
patchFlag: '1',
|
||||||
@ -298,6 +298,37 @@ describe('compiler: codegen', () => {
|
|||||||
expect(code).toMatchSnapshot()
|
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[])', () => {
|
test('Element (callExpression + objectExpression + TemplateChildNode[])', () => {
|
||||||
const { code } = generate(
|
const { code } = generate(
|
||||||
createRoot({
|
createRoot({
|
||||||
|
@ -62,7 +62,7 @@ export function createElementWithCodegen(
|
|||||||
dynamicProps,
|
dynamicProps,
|
||||||
directives: undefined,
|
directives: undefined,
|
||||||
isBlock: false,
|
isBlock: false,
|
||||||
isForBlock: false,
|
disableTracking: false,
|
||||||
loc: locStub
|
loc: locStub
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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`] = `
|
exports[`compiler: v-for codegen v-if + v-for 1`] = `
|
||||||
"const _Vue = Vue
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
@ -560,13 +560,16 @@ describe('compiler: v-for', () => {
|
|||||||
function assertSharedCodegen(
|
function assertSharedCodegen(
|
||||||
node: ForCodegenNode,
|
node: ForCodegenNode,
|
||||||
keyed: boolean = false,
|
keyed: boolean = false,
|
||||||
customReturn: boolean = false
|
customReturn: boolean = false,
|
||||||
|
disableTracking: boolean = true
|
||||||
) {
|
) {
|
||||||
expect(node).toMatchObject({
|
expect(node).toMatchObject({
|
||||||
type: NodeTypes.VNODE_CALL,
|
type: NodeTypes.VNODE_CALL,
|
||||||
tag: FRAGMENT,
|
tag: FRAGMENT,
|
||||||
isForBlock: true,
|
disableTracking,
|
||||||
patchFlag: keyed
|
patchFlag: !disableTracking
|
||||||
|
? genFlagText(PatchFlags.STABLE_FRAGMENT)
|
||||||
|
: keyed
|
||||||
? genFlagText(PatchFlags.KEYED_FRAGMENT)
|
? genFlagText(PatchFlags.KEYED_FRAGMENT)
|
||||||
: genFlagText(PatchFlags.UNKEYED_FRAGMENT),
|
: genFlagText(PatchFlags.UNKEYED_FRAGMENT),
|
||||||
children: {
|
children: {
|
||||||
@ -580,7 +583,7 @@ describe('compiler: v-for', () => {
|
|||||||
? {}
|
? {}
|
||||||
: {
|
: {
|
||||||
type: NodeTypes.VNODE_CALL,
|
type: NodeTypes.VNODE_CALL,
|
||||||
isBlock: true
|
isBlock: disableTracking
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -658,6 +661,43 @@ describe('compiler: v-for', () => {
|
|||||||
expect(generate(root).code).toMatchSnapshot()
|
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', () => {
|
test('template v-for', () => {
|
||||||
const {
|
const {
|
||||||
root,
|
root,
|
||||||
@ -777,7 +817,7 @@ describe('compiler: v-for', () => {
|
|||||||
key: `[0]`
|
key: `[0]`
|
||||||
}),
|
}),
|
||||||
isBlock: true,
|
isBlock: true,
|
||||||
isForBlock: true,
|
disableTracking: true,
|
||||||
patchFlag: genFlagText(PatchFlags.UNKEYED_FRAGMENT),
|
patchFlag: genFlagText(PatchFlags.UNKEYED_FRAGMENT),
|
||||||
children: {
|
children: {
|
||||||
type: NodeTypes.JS_CALL_EXPRESSION,
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
@ -281,7 +281,7 @@ export interface VNodeCall extends Node {
|
|||||||
dynamicProps: string | undefined
|
dynamicProps: string | undefined
|
||||||
directives: DirectiveArguments | undefined
|
directives: DirectiveArguments | undefined
|
||||||
isBlock: boolean
|
isBlock: boolean
|
||||||
isForBlock: boolean
|
disableTracking: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// JS Node Types ---------------------------------------------------------------
|
// JS Node Types ---------------------------------------------------------------
|
||||||
@ -492,7 +492,7 @@ export interface ForCodegenNode extends VNodeCall {
|
|||||||
props: undefined
|
props: undefined
|
||||||
children: ForRenderListExpression
|
children: ForRenderListExpression
|
||||||
patchFlag: string
|
patchFlag: string
|
||||||
isForBlock: true
|
disableTracking: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ForRenderListExpression extends CallExpression {
|
export interface ForRenderListExpression extends CallExpression {
|
||||||
@ -543,7 +543,7 @@ export function createVNodeCall(
|
|||||||
dynamicProps?: VNodeCall['dynamicProps'],
|
dynamicProps?: VNodeCall['dynamicProps'],
|
||||||
directives?: VNodeCall['directives'],
|
directives?: VNodeCall['directives'],
|
||||||
isBlock: VNodeCall['isBlock'] = false,
|
isBlock: VNodeCall['isBlock'] = false,
|
||||||
isForBlock: VNodeCall['isForBlock'] = false,
|
disableTracking: VNodeCall['disableTracking'] = false,
|
||||||
loc = locStub
|
loc = locStub
|
||||||
): VNodeCall {
|
): VNodeCall {
|
||||||
if (context) {
|
if (context) {
|
||||||
@ -567,7 +567,7 @@ export function createVNodeCall(
|
|||||||
dynamicProps,
|
dynamicProps,
|
||||||
directives,
|
directives,
|
||||||
isBlock,
|
isBlock,
|
||||||
isForBlock,
|
disableTracking,
|
||||||
loc
|
loc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -698,13 +698,13 @@ function genVNodeCall(node: VNodeCall, context: CodegenContext) {
|
|||||||
dynamicProps,
|
dynamicProps,
|
||||||
directives,
|
directives,
|
||||||
isBlock,
|
isBlock,
|
||||||
isForBlock
|
disableTracking
|
||||||
} = node
|
} = node
|
||||||
if (directives) {
|
if (directives) {
|
||||||
push(helper(WITH_DIRECTIVES) + `(`)
|
push(helper(WITH_DIRECTIVES) + `(`)
|
||||||
}
|
}
|
||||||
if (isBlock) {
|
if (isBlock) {
|
||||||
push(`(${helper(OPEN_BLOCK)}(${isForBlock ? `true` : ``}), `)
|
push(`(${helper(OPEN_BLOCK)}(${disableTracking ? `true` : ``}), `)
|
||||||
}
|
}
|
||||||
if (pure) {
|
if (pure) {
|
||||||
push(PURE_ANNOTATION)
|
push(PURE_ANNOTATION)
|
||||||
|
@ -203,7 +203,7 @@ export const transformElement: NodeTransform = (node, context) => {
|
|||||||
vnodeDynamicProps,
|
vnodeDynamicProps,
|
||||||
vnodeDirectives,
|
vnodeDirectives,
|
||||||
!!shouldUseBlock,
|
!!shouldUseBlock,
|
||||||
false /* isForBlock */,
|
false /* disableTracking */,
|
||||||
node.loc
|
node.loc
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,12 @@ export const transformFor = createStructuralDirectiveTransform(
|
|||||||
forNode.source
|
forNode.source
|
||||||
]) as ForRenderListExpression
|
]) as ForRenderListExpression
|
||||||
const keyProp = findProp(node, `key`)
|
const keyProp = findProp(node, `key`)
|
||||||
const fragmentFlag = keyProp
|
const isStableFragment =
|
||||||
|
forNode.source.type === NodeTypes.SIMPLE_EXPRESSION &&
|
||||||
|
forNode.source.isConstant
|
||||||
|
const fragmentFlag = isStableFragment
|
||||||
|
? PatchFlags.STABLE_FRAGMENT
|
||||||
|
: keyProp
|
||||||
? PatchFlags.KEYED_FRAGMENT
|
? PatchFlags.KEYED_FRAGMENT
|
||||||
: PatchFlags.UNKEYED_FRAGMENT
|
: PatchFlags.UNKEYED_FRAGMENT
|
||||||
forNode.codegenNode = createVNodeCall(
|
forNode.codegenNode = createVNodeCall(
|
||||||
@ -67,7 +72,7 @@ export const transformFor = createStructuralDirectiveTransform(
|
|||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
true /* isBlock */,
|
true /* isBlock */,
|
||||||
true /* isForBlock */,
|
!isStableFragment /* disableTracking */,
|
||||||
node.loc
|
node.loc
|
||||||
) as ForCodegenNode
|
) as ForCodegenNode
|
||||||
|
|
||||||
@ -122,10 +127,12 @@ export const transformFor = createStructuralDirectiveTransform(
|
|||||||
// but mark it as a block.
|
// but mark it as a block.
|
||||||
childBlock = (children[0] as PlainElementNode)
|
childBlock = (children[0] as PlainElementNode)
|
||||||
.codegenNode as VNodeCall
|
.codegenNode as VNodeCall
|
||||||
childBlock.isBlock = true
|
childBlock.isBlock = !isStableFragment
|
||||||
|
if (childBlock.isBlock) {
|
||||||
helper(OPEN_BLOCK)
|
helper(OPEN_BLOCK)
|
||||||
helper(CREATE_BLOCK)
|
helper(CREATE_BLOCK)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
renderExp.arguments.push(createFunctionExpression(
|
renderExp.arguments.push(createFunctionExpression(
|
||||||
createForLoopParams(forNode.parseResult),
|
createForLoopParams(forNode.parseResult),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user