Skip to content

Commit d69afa2

Browse files
authored
perf: improve VNode creation performance with compiler hints (#3334)
1 parent 9a5bdb1 commit d69afa2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1130
-686
lines changed

Diff for: packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap

+4-4
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@ exports[`compiler: codegen Element (callExpression + objectExpression + Template
4848
"
4949
return function render(_ctx, _cache) {
5050
with (_ctx) {
51-
return _createVNode(\\"div\\", {
51+
return _createElementVNode(\\"div\\", {
5252
id: \\"foo\\",
5353
[prop]: bar,
5454
[foo + bar]: bar
5555
}, [
56-
_createVNode(\\"p\\", { \\"some-key\\": \\"foo\\" })
56+
_createElementVNode(\\"p\\", { \\"some-key\\": \\"foo\\" })
5757
], 16)
5858
}
5959
}"
@@ -98,7 +98,7 @@ exports[`compiler: codegen forNode 1`] = `
9898
"
9999
return function render(_ctx, _cache) {
100100
with (_ctx) {
101-
return (_openBlock(true), _createBlock(_Fragment, null, _renderList(), 1))
101+
return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(), 1))
102102
}
103103
}"
104104
`;
@@ -107,7 +107,7 @@ exports[`compiler: codegen forNode with constant expression 1`] = `
107107
"
108108
return function render(_ctx, _cache) {
109109
with (_ctx) {
110-
return (_openBlock(), _createBlock(_Fragment, null, _renderList(), 64 /* STABLE_FRAGMENT */))
110+
return (_openBlock(), _createElementBlock(_Fragment, null, _renderList(), 64 /* STABLE_FRAGMENT */))
111111
}
112112
}"
113113
`;

Diff for: packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap

+24-24
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,21 @@ exports[`compiler: integration tests function mode 1`] = `
55
66
return function render(_ctx, _cache) {
77
with (_ctx) {
8-
const { toDisplayString: _toDisplayString, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode, createTextVNode: _createTextVNode, Fragment: _Fragment, renderList: _renderList, createVNode: _createVNode } = _Vue
8+
const { toDisplayString: _toDisplayString, openBlock: _openBlock, createElementBlock: _createElementBlock, createCommentVNode: _createCommentVNode, createTextVNode: _createTextVNode, Fragment: _Fragment, renderList: _renderList, createElementVNode: _createElementVNode, normalizeClass: _normalizeClass } = _Vue
99
10-
return (_openBlock(), _createBlock(\\"div\\", {
10+
return (_openBlock(), _createElementBlock(\\"div\\", {
1111
id: \\"foo\\",
12-
class: bar.baz
12+
class: _normalizeClass(bar.baz)
1313
}, [
1414
_createTextVNode(_toDisplayString(world.burn()) + \\" \\", 1 /* TEXT */),
1515
ok
16-
? (_openBlock(), _createBlock(\\"div\\", { key: 0 }, \\"yes\\"))
17-
: (_openBlock(), _createBlock(_Fragment, { key: 1 }, [
16+
? (_openBlock(), _createElementBlock(\\"div\\", { key: 0 }, \\"yes\\"))
17+
: (_openBlock(), _createElementBlock(_Fragment, { key: 1 }, [
1818
_createTextVNode(\\"no\\")
1919
], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */)),
20-
(_openBlock(true), _createBlock(_Fragment, null, _renderList(list, (value, index) => {
21-
return (_openBlock(), _createBlock(\\"div\\", null, [
22-
_createVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */)
20+
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(list, (value, index) => {
21+
return (_openBlock(), _createElementBlock(\\"div\\", null, [
22+
_createElementVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */)
2323
]))
2424
}), 256 /* UNKEYED_FRAGMENT */))
2525
], 2 /* CLASS */))
@@ -28,45 +28,45 @@ return function render(_ctx, _cache) {
2828
`;
2929

3030
exports[`compiler: integration tests function mode w/ prefixIdentifiers: true 1`] = `
31-
"const { toDisplayString: _toDisplayString, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode, createTextVNode: _createTextVNode, Fragment: _Fragment, renderList: _renderList, createVNode: _createVNode } = Vue
31+
"const { toDisplayString: _toDisplayString, openBlock: _openBlock, createElementBlock: _createElementBlock, createCommentVNode: _createCommentVNode, createTextVNode: _createTextVNode, Fragment: _Fragment, renderList: _renderList, createElementVNode: _createElementVNode, normalizeClass: _normalizeClass } = Vue
3232
3333
return function render(_ctx, _cache) {
34-
return (_openBlock(), _createBlock(\\"div\\", {
34+
return (_openBlock(), _createElementBlock(\\"div\\", {
3535
id: \\"foo\\",
36-
class: _ctx.bar.baz
36+
class: _normalizeClass(_ctx.bar.baz)
3737
}, [
3838
_createTextVNode(_toDisplayString(_ctx.world.burn()) + \\" \\", 1 /* TEXT */),
3939
(_ctx.ok)
40-
? (_openBlock(), _createBlock(\\"div\\", { key: 0 }, \\"yes\\"))
41-
: (_openBlock(), _createBlock(_Fragment, { key: 1 }, [
40+
? (_openBlock(), _createElementBlock(\\"div\\", { key: 0 }, \\"yes\\"))
41+
: (_openBlock(), _createElementBlock(_Fragment, { key: 1 }, [
4242
_createTextVNode(\\"no\\")
4343
], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */)),
44-
(_openBlock(true), _createBlock(_Fragment, null, _renderList(_ctx.list, (value, index) => {
45-
return (_openBlock(), _createBlock(\\"div\\", null, [
46-
_createVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */)
44+
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.list, (value, index) => {
45+
return (_openBlock(), _createElementBlock(\\"div\\", null, [
46+
_createElementVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */)
4747
]))
4848
}), 256 /* UNKEYED_FRAGMENT */))
4949
], 2 /* CLASS */))
5050
}"
5151
`;
5252

5353
exports[`compiler: integration tests module mode 1`] = `
54-
"import { toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock, createCommentVNode as _createCommentVNode, createTextVNode as _createTextVNode, Fragment as _Fragment, renderList as _renderList, createVNode as _createVNode } from \\"vue\\"
54+
"import { toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock, createCommentVNode as _createCommentVNode, createTextVNode as _createTextVNode, Fragment as _Fragment, renderList as _renderList, createElementVNode as _createElementVNode, normalizeClass as _normalizeClass } from \\"vue\\"
5555
5656
export function render(_ctx, _cache) {
57-
return (_openBlock(), _createBlock(\\"div\\", {
57+
return (_openBlock(), _createElementBlock(\\"div\\", {
5858
id: \\"foo\\",
59-
class: _ctx.bar.baz
59+
class: _normalizeClass(_ctx.bar.baz)
6060
}, [
6161
_createTextVNode(_toDisplayString(_ctx.world.burn()) + \\" \\", 1 /* TEXT */),
6262
(_ctx.ok)
63-
? (_openBlock(), _createBlock(\\"div\\", { key: 0 }, \\"yes\\"))
64-
: (_openBlock(), _createBlock(_Fragment, { key: 1 }, [
63+
? (_openBlock(), _createElementBlock(\\"div\\", { key: 0 }, \\"yes\\"))
64+
: (_openBlock(), _createElementBlock(_Fragment, { key: 1 }, [
6565
_createTextVNode(\\"no\\")
6666
], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */)),
67-
(_openBlock(true), _createBlock(_Fragment, null, _renderList(_ctx.list, (value, index) => {
68-
return (_openBlock(), _createBlock(\\"div\\", null, [
69-
_createVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */)
67+
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.list, (value, index) => {
68+
return (_openBlock(), _createElementBlock(\\"div\\", null, [
69+
_createElementVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */)
7070
]))
7171
}), 256 /* UNKEYED_FRAGMENT */))
7272
], 2 /* CLASS */))

Diff for: packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap

+11-11
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

33
exports[`scopeId compiler support should push scopeId for hoisted nodes 1`] = `
4-
"import { createVNode as _createVNode, toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from \\"vue\\"
4+
"import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, openBlock as _openBlock, createElementBlock as _createElementBlock, withScopeId as _withScopeId, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from \\"vue\\"
55
const _withId = /*#__PURE__*/_withScopeId(\\"test\\")
66
77
_pushScopeId(\\"test\\")
8-
const _hoisted_1 = /*#__PURE__*/_createVNode(\\"div\\", null, \\"hello\\", -1 /* HOISTED */)
9-
const _hoisted_2 = /*#__PURE__*/_createVNode(\\"div\\", null, \\"world\\", -1 /* HOISTED */)
8+
const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"div\\", null, \\"hello\\", -1 /* HOISTED */)
9+
const _hoisted_2 = /*#__PURE__*/_createElementVNode(\\"div\\", null, \\"world\\", -1 /* HOISTED */)
1010
_popScopeId()
1111
1212
export const render = /*#__PURE__*/_withId((_ctx, _cache) => {
13-
return (_openBlock(), _createBlock(\\"div\\", null, [
13+
return (_openBlock(), _createElementBlock(\\"div\\", null, [
1414
_hoisted_1,
1515
_createTextVNode(_toDisplayString(_ctx.foo), 1 /* TEXT */),
1616
_hoisted_2
@@ -19,23 +19,23 @@ export const render = /*#__PURE__*/_withId((_ctx, _cache) => {
1919
`;
2020

2121
exports[`scopeId compiler support should wrap default slot 1`] = `
22-
"import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
22+
"import { createElementVNode as _createElementVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
2323
const _withId = /*#__PURE__*/_withScopeId(\\"test\\")
2424
2525
export const render = /*#__PURE__*/_withId((_ctx, _cache) => {
2626
const _component_Child = _resolveComponent(\\"Child\\")
2727
2828
return (_openBlock(), _createBlock(_component_Child, null, {
2929
default: _withId(() => [
30-
_createVNode(\\"div\\")
30+
_createElementVNode(\\"div\\")
3131
]),
3232
_: 1 /* STABLE */
3333
}))
3434
})"
3535
`;
3636

3737
exports[`scopeId compiler support should wrap dynamic slots 1`] = `
38-
"import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, renderList as _renderList, createSlots as _createSlots, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
38+
"import { createElementVNode as _createElementVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, renderList as _renderList, createSlots as _createSlots, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
3939
const _withId = /*#__PURE__*/_withScopeId(\\"test\\")
4040
4141
export const render = /*#__PURE__*/_withId((_ctx, _cache) => {
@@ -46,15 +46,15 @@ export const render = /*#__PURE__*/_withId((_ctx, _cache) => {
4646
? {
4747
name: \\"foo\\",
4848
fn: _withId(() => [
49-
_createVNode(\\"div\\")
49+
_createElementVNode(\\"div\\")
5050
])
5151
}
5252
: undefined,
5353
_renderList(_ctx.list, (i) => {
5454
return {
5555
name: i,
5656
fn: _withId(() => [
57-
_createVNode(\\"div\\")
57+
_createElementVNode(\\"div\\")
5858
])
5959
}
6060
})
@@ -63,7 +63,7 @@ export const render = /*#__PURE__*/_withId((_ctx, _cache) => {
6363
`;
6464

6565
exports[`scopeId compiler support should wrap named slots 1`] = `
66-
"import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
66+
"import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createElementVNode as _createElementVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
6767
const _withId = /*#__PURE__*/_withScopeId(\\"test\\")
6868
6969
export const render = /*#__PURE__*/_withId((_ctx, _cache) => {
@@ -74,7 +74,7 @@ export const render = /*#__PURE__*/_withId((_ctx, _cache) => {
7474
_createTextVNode(_toDisplayString(msg), 1 /* TEXT */)
7575
]),
7676
bar: _withId(() => [
77-
_createVNode(\\"div\\")
77+
_createElementVNode(\\"div\\")
7878
]),
7979
_: 1 /* STABLE */
8080
}))

Diff for: packages/compiler-core/__tests__/codegen.spec.ts

+14-13
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ import {
3131
RESOLVE_COMPONENT,
3232
CREATE_COMMENT,
3333
FRAGMENT,
34-
RENDER_LIST
34+
RENDER_LIST,
35+
CREATE_ELEMENT_VNODE
3536
} from '../src/runtimeHelpers'
3637
import { createElementWithCodegen, genFlagText } from './testUtils'
3738
import { PatchFlags } from '@vue/shared'
@@ -395,12 +396,12 @@ describe('compiler: codegen', () => {
395396
})
396397
)
397398
expect(code).toMatch(`
398-
return _${helperNameMap[CREATE_VNODE]}("div", {
399+
return _${helperNameMap[CREATE_ELEMENT_VNODE]}("div", {
399400
id: "foo",
400401
[prop]: bar,
401402
[foo + bar]: bar
402403
}, [
403-
_${helperNameMap[CREATE_VNODE]}("p", { "some-key": "foo" })
404+
_${helperNameMap[CREATE_ELEMENT_VNODE]}("p", { "some-key": "foo" })
404405
], ${PatchFlags.FULL_PROPS})`)
405406
expect(code).toMatchSnapshot()
406407
})
@@ -658,43 +659,43 @@ describe('compiler: codegen', () => {
658659

659660
test('tag only', () => {
660661
expect(genCode(createVNodeCall(null, `"div"`))).toMatchInlineSnapshot(`
661-
"return _createVNode(\\"div\\")
662+
"return _createElementVNode(\\"div\\")
662663
"
663664
`)
664665
expect(genCode(createVNodeCall(null, FRAGMENT))).toMatchInlineSnapshot(`
665-
"return _createVNode(_Fragment)
666+
"return _createElementVNode(_Fragment)
666667
"
667668
`)
668669
})
669670

670671
test('with props', () => {
671672
expect(genCode(createVNodeCall(null, `"div"`, mockProps)))
672673
.toMatchInlineSnapshot(`
673-
"return _createVNode(\\"div\\", { foo: \\"bar\\" })
674+
"return _createElementVNode(\\"div\\", { foo: \\"bar\\" })
674675
"
675676
`)
676677
})
677678

678679
test('with children, no props', () => {
679680
expect(genCode(createVNodeCall(null, `"div"`, undefined, mockChildren)))
680681
.toMatchInlineSnapshot(`
681-
"return _createVNode(\\"div\\", null, children)
682+
"return _createElementVNode(\\"div\\", null, children)
682683
"
683684
`)
684685
})
685686

686687
test('with children + props', () => {
687688
expect(genCode(createVNodeCall(null, `"div"`, mockProps, mockChildren)))
688689
.toMatchInlineSnapshot(`
689-
"return _createVNode(\\"div\\", { foo: \\"bar\\" }, children)
690+
"return _createElementVNode(\\"div\\", { foo: \\"bar\\" }, children)
690691
"
691692
`)
692693
})
693694

694695
test('with patchFlag and no children/props', () => {
695696
expect(genCode(createVNodeCall(null, `"div"`, undefined, undefined, '1')))
696697
.toMatchInlineSnapshot(`
697-
"return _createVNode(\\"div\\", null, null, 1)
698+
"return _createElementVNode(\\"div\\", null, null, 1)
698699
"
699700
`)
700701
})
@@ -714,7 +715,7 @@ describe('compiler: codegen', () => {
714715
)
715716
)
716717
).toMatchInlineSnapshot(`
717-
"return (_openBlock(), _createBlock(\\"div\\", { foo: \\"bar\\" }, children))
718+
"return (_openBlock(), _createElementBlock(\\"div\\", { foo: \\"bar\\" }, children))
718719
"
719720
`)
720721
})
@@ -735,7 +736,7 @@ describe('compiler: codegen', () => {
735736
)
736737
)
737738
).toMatchInlineSnapshot(`
738-
"return (_openBlock(true), _createBlock(\\"div\\", { foo: \\"bar\\" }, children))
739+
"return (_openBlock(true), _createElementBlock(\\"div\\", { foo: \\"bar\\" }, children))
739740
"
740741
`)
741742
})
@@ -754,7 +755,7 @@ describe('compiler: codegen', () => {
754755
)
755756
)
756757
).toMatchInlineSnapshot(`
757-
"return _withDirectives(_createVNode(\\"div\\", { foo: \\"bar\\" }, children), [
758+
"return _withDirectives(_createElementVNode(\\"div\\", { foo: \\"bar\\" }, children), [
758759
[foo, bar]
759760
])
760761
"
@@ -776,7 +777,7 @@ describe('compiler: codegen', () => {
776777
)
777778
)
778779
).toMatchInlineSnapshot(`
779-
"return _withDirectives((_openBlock(), _createBlock(\\"div\\", { foo: \\"bar\\" }, children)), [
780+
"return _withDirectives((_openBlock(), _createElementBlock(\\"div\\", { foo: \\"bar\\" }, children)), [
780781
[foo, bar]
781782
])
782783
"

Diff for: packages/compiler-core/__tests__/scopeId.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,10 @@ describe('scopeId compiler support', () => {
7373
expect(code).toMatch(
7474
[
7575
`_pushScopeId("test")`,
76-
`const _hoisted_1 = /*#__PURE__*/_createVNode("div", null, "hello", ${genFlagText(
76+
`const _hoisted_1 = /*#__PURE__*/_createElementVNode("div", null, "hello", ${genFlagText(
7777
PatchFlags.HOISTED
7878
)})`,
79-
`const _hoisted_2 = /*#__PURE__*/_createVNode("div", null, "world", ${genFlagText(
79+
`const _hoisted_2 = /*#__PURE__*/_createElementVNode("div", null, "world", ${genFlagText(
8080
PatchFlags.HOISTED
8181
)})`,
8282
`_popScopeId()`

Diff for: packages/compiler-core/__tests__/testUtils.ts

+16-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ import {
66
ElementTypes,
77
VNodeCall
88
} from '../src'
9-
import { isString, PatchFlags, PatchFlagNames, isArray } from '@vue/shared'
9+
import {
10+
isString,
11+
PatchFlags,
12+
PatchFlagNames,
13+
isArray,
14+
ShapeFlags
15+
} from '@vue/shared'
1016

1117
const leadingBracketRE = /^\[/
1218
const bracketsRE = /^\[|\]$/g
@@ -63,19 +69,25 @@ export function createElementWithCodegen(
6369
directives: undefined,
6470
isBlock: false,
6571
disableTracking: false,
72+
isComponent: false,
73+
shapeFlag: ShapeFlags.ELEMENT + ``,
6674
loc: locStub
6775
}
6876
}
6977
}
7078

71-
export function genFlagText(flag: PatchFlags | PatchFlags[]) {
79+
type Flags = PatchFlags | ShapeFlags
80+
export function genFlagText(
81+
flag: Flags | Flags[],
82+
names: { [k: number]: string } = PatchFlagNames
83+
) {
7284
if (isArray(flag)) {
7385
let f = 0
7486
flag.forEach(ff => {
7587
f |= ff
7688
})
77-
return `${f} /* ${flag.map(f => PatchFlagNames[f]).join(', ')} */`
89+
return `${f} /* ${flag.map(f => names[f]).join(', ')} */`
7890
} else {
79-
return `${flag} /* ${PatchFlagNames[flag]} */`
91+
return `${flag} /* ${names[flag]} */`
8092
}
8193
}

0 commit comments

Comments
 (0)