Skip to content

Commit e61cedf

Browse files
perf(runtime-vapor): use setAttr or setDOMProp instead of setDynamicProp when possible (#291)
Co-authored-by: Doctor Wu <[email protected]>
1 parent 9a2158d commit e61cedf

File tree

9 files changed

+284
-51
lines changed

9 files changed

+284
-51
lines changed

packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap

+4-4
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
151151
`;
152152
153153
exports[`compile > directives > v-pre > should not affect siblings after it 1`] = `
154-
"import { resolveComponent as _resolveComponent, createComponent as _createComponent, createTextNode as _createTextNode, insert as _insert, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
154+
"import { resolveComponent as _resolveComponent, createComponent as _createComponent, createTextNode as _createTextNode, insert as _insert, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
155155
const t0 = _template("<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div>")
156156
const t1 = _template("<div></div>")
157157
@@ -162,7 +162,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
162162
const n1 = _createComponent(_component_Comp)
163163
const n2 = _createTextNode(() => [_ctx.bar])
164164
_insert([n1, n2], n3)
165-
_renderEffect(() => _setDynamicProp(n3, "id", _ctx.foo))
165+
_renderEffect(() => _setDOMProp(n3, "id", _ctx.foo))
166166
return [n0, n3]
167167
}"
168168
`;
@@ -177,7 +177,7 @@ export function render(_ctx) {
177177
`;
178178
179179
exports[`compile > dynamic root nodes and interpolation 1`] = `
180-
"import { delegate as _delegate, setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setText as _setText, setDynamicProp as _setDynamicProp, delegateEvents as _delegateEvents, template as _template } from 'vue/vapor';
180+
"import { delegate as _delegate, setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setText as _setText, setDOMProp as _setDOMProp, delegateEvents as _delegateEvents, template as _template } from 'vue/vapor';
181181
const t0 = _template("<button></button>")
182182
_delegateEvents("click")
183183
@@ -186,7 +186,7 @@ export function render(_ctx) {
186186
_delegate(n0, "click", () => _ctx.handleClick)
187187
_setInheritAttrs(["id"])
188188
_renderEffect(() => _setText(n0, _ctx.count, "foo", _ctx.count, "foo", _ctx.count))
189-
_renderEffect(() => _setDynamicProp(n0, "id", _ctx.count, true))
189+
_renderEffect(() => _setDOMProp(n0, "id", _ctx.count, true))
190190
return n0
191191
}"
192192
`;

packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap

+90-4
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,100 @@ export function render(_ctx) {
121121
}"
122122
`;
123123

124+
exports[`compiler v-bind > HTML global attributes should set as dom prop 1`] = `
125+
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
126+
const t0 = _template("<div></div>")
127+
128+
export function render(_ctx) {
129+
const n0 = t0()
130+
_setInheritAttrs(["id", "title", "lang", "dir", "tabindex"])
131+
_renderEffect(() => _setDOMProp(n0, "id", _ctx.id, true))
132+
_renderEffect(() => _setDOMProp(n0, "title", _ctx.title, true))
133+
_renderEffect(() => _setDOMProp(n0, "lang", _ctx.lang, true))
134+
_renderEffect(() => _setDOMProp(n0, "dir", _ctx.dir, true))
135+
_renderEffect(() => _setDOMProp(n0, "tabindex", _ctx.tabindex, true))
136+
return n0
137+
}"
138+
`;
139+
140+
exports[`compiler v-bind > MathML global attributes should set as dom prop 1`] = `
141+
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
142+
const t0 = _template("<math></math>")
143+
144+
export function render(_ctx) {
145+
const n0 = t0()
146+
_setInheritAttrs(["autofucus", "dir", "displaystyle", "mathcolor", "tabindex"])
147+
_renderEffect(() => _setDOMProp(n0, "autofucus", _ctx.autofucus, true))
148+
_renderEffect(() => _setDOMProp(n0, "dir", _ctx.dir, true))
149+
_renderEffect(() => _setDOMProp(n0, "displaystyle", _ctx.displaystyle, true))
150+
_renderEffect(() => _setDOMProp(n0, "mathcolor", _ctx.mathcolor, true))
151+
_renderEffect(() => _setDOMProp(n0, "tabindex", _ctx.tabindex, true))
152+
return n0
153+
}"
154+
`;
155+
156+
exports[`compiler v-bind > SVG global attributes should set as dom prop 1`] = `
157+
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
158+
const t0 = _template("<svg></svg>")
159+
160+
export function render(_ctx) {
161+
const n0 = t0()
162+
_setInheritAttrs(["id", "lang", "tabindex"])
163+
_renderEffect(() => _setDOMProp(n0, "id", _ctx.id, true))
164+
_renderEffect(() => _setDOMProp(n0, "lang", _ctx.lang, true))
165+
_renderEffect(() => _setDOMProp(n0, "tabindex", _ctx.tabindex, true))
166+
return n0
167+
}"
168+
`;
169+
170+
exports[`compiler v-bind > attributes must be set as attribute 1`] = `
171+
"import { renderEffect as _renderEffect, setAttr as _setAttr, template as _template } from 'vue/vapor';
172+
const t0 = _template("<div></div>")
173+
const t1 = _template("<input>")
174+
const t2 = _template("<textarea></textarea>")
175+
const t3 = _template("<img>")
176+
const t4 = _template("<video></video>")
177+
const t5 = _template("<canvas></canvas>")
178+
const t6 = _template("<source>")
179+
180+
export function render(_ctx) {
181+
const n0 = t0()
182+
const n1 = t1()
183+
const n2 = t2()
184+
const n3 = t3()
185+
const n4 = t4()
186+
const n5 = t5()
187+
const n6 = t6()
188+
_renderEffect(() => _setAttr(n0, "spellcheck", _ctx.spellcheck))
189+
_renderEffect(() => _setAttr(n0, "draggable", _ctx.draggable))
190+
_renderEffect(() => _setAttr(n0, "translate", _ctx.translate))
191+
_renderEffect(() => _setAttr(n0, "form", _ctx.form))
192+
_renderEffect(() => _setAttr(n1, "list", _ctx.list))
193+
_renderEffect(() => _setAttr(n2, "type", _ctx.type))
194+
_renderEffect(() => {
195+
_setAttr(n3, "width", _ctx.width)
196+
_setAttr(n4, "width", _ctx.width)
197+
_setAttr(n5, "width", _ctx.width)
198+
_setAttr(n6, "width", _ctx.width)
199+
})
200+
_renderEffect(() => {
201+
_setAttr(n3, "height", _ctx.height)
202+
_setAttr(n4, "height", _ctx.height)
203+
_setAttr(n5, "height", _ctx.height)
204+
_setAttr(n6, "height", _ctx.height)
205+
})
206+
return [n0, n1, n2, n3, n4, n5, n6]
207+
}"
208+
`;
209+
124210
exports[`compiler v-bind > basic 1`] = `
125-
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
211+
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
126212
const t0 = _template("<div></div>")
127213
128214
export function render(_ctx) {
129215
const n0 = t0()
130216
_setInheritAttrs(["id"])
131-
_renderEffect(() => _setDynamicProp(n0, "id", _ctx.id, true))
217+
_renderEffect(() => _setDOMProp(n0, "id", _ctx.id, true))
132218
return n0
133219
}"
134220
`;
@@ -170,13 +256,13 @@ export function render(_ctx) {
170256
`;
171257
172258
exports[`compiler v-bind > no expression 1`] = `
173-
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
259+
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
174260
const t0 = _template("<div></div>")
175261
176262
export function render(_ctx) {
177263
const n0 = t0()
178264
_setInheritAttrs(["id"])
179-
_renderEffect(() => _setDynamicProp(n0, "id", _ctx.id, true))
265+
_renderEffect(() => _setDOMProp(n0, "id", _ctx.id, true))
180266
return n0
181267
}"
182268
`;

packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

33
exports[`compiler: v-once > as root node 1`] = `
4-
"import { setDynamicProp as _setDynamicProp, setInheritAttrs as _setInheritAttrs, template as _template } from 'vue/vapor';
4+
"import { setDOMProp as _setDOMProp, setInheritAttrs as _setInheritAttrs, template as _template } from 'vue/vapor';
55
const t0 = _template("<div></div>")
66
77
export function render(_ctx) {
88
const n0 = t0()
9-
_setDynamicProp(n0, "id", _ctx.foo, true)
9+
_setDOMProp(n0, "id", _ctx.foo, true)
1010
_setInheritAttrs(["id"])
1111
return n0
1212
}"
@@ -52,13 +52,13 @@ export function render(_ctx) {
5252
`;
5353

5454
exports[`compiler: v-once > on nested plain element 1`] = `
55-
"import { setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
55+
"import { setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
5656
const t0 = _template("<div><div></div></div>")
5757
5858
export function render(_ctx) {
5959
const n1 = t0()
6060
const n0 = n1.firstChild
61-
_setDynamicProp(n0, "id", _ctx.foo)
61+
_setDOMProp(n0, "id", _ctx.foo)
6262
return n1
6363
}"
6464
`;

packages/compiler-vapor/__tests__/transforms/vBind.spec.ts

+69-2
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ describe('compiler v-bind', () => {
7474
})
7575

7676
expect(code).matchSnapshot()
77-
expect(code).contains('_setDynamicProp(n0, "id", _ctx.id, true)')
77+
expect(code).contains('_setDOMProp(n0, "id", _ctx.id, true)')
7878
})
7979

8080
test('no expression', () => {
@@ -104,7 +104,7 @@ describe('compiler v-bind', () => {
104104
],
105105
},
106106
})
107-
expect(code).contains('_setDynamicProp(n0, "id", _ctx.id, true)')
107+
expect(code).contains('_setDOMProp(n0, "id", _ctx.id, true)')
108108
})
109109

110110
test('no expression (shorthand)', () => {
@@ -527,6 +527,73 @@ describe('compiler v-bind', () => {
527527
expect(code).contains('_setAttr(n0, "foo-bar", _ctx.fooBar, true)')
528528
})
529529

530+
test('attributes must be set as attribute', () => {
531+
const { code } = compileWithVBind(`
532+
<div :spellcheck :draggable :translate :form />
533+
<input :list="list" />
534+
<textarea :type="type" />
535+
<img :width="width" :height="height"/>
536+
<video :width="width" :height="height"/>
537+
<canvas :width="width" :height="height"/>
538+
<source :width="width" :height="height"/>
539+
`)
540+
541+
expect(code).matchSnapshot()
542+
expect(code).contains('_setAttr(n0, "spellcheck", _ctx.spellcheck)')
543+
expect(code).contains('_setAttr(n0, "draggable", _ctx.draggable)')
544+
expect(code).contains('_setAttr(n0, "translate", _ctx.translate)')
545+
expect(code).contains('_setAttr(n0, "form", _ctx.form)')
546+
expect(code).contains('_setAttr(n1, "list", _ctx.list)')
547+
expect(code).contains('_setAttr(n2, "type", _ctx.type)')
548+
expect(code).contains('_setAttr(n3, "width", _ctx.width)')
549+
expect(code).contains('_setAttr(n3, "height", _ctx.height)')
550+
expect(code).contains('_setAttr(n4, "width", _ctx.width)')
551+
expect(code).contains('_setAttr(n4, "height", _ctx.height)')
552+
expect(code).contains('_setAttr(n5, "width", _ctx.width)')
553+
expect(code).contains('_setAttr(n5, "height", _ctx.height)')
554+
expect(code).contains('_setAttr(n6, "width", _ctx.width)')
555+
expect(code).contains('_setAttr(n6, "height", _ctx.height)')
556+
})
557+
558+
test('HTML global attributes should set as dom prop', () => {
559+
const { code } = compileWithVBind(`
560+
<div :id="id" :title="title" :lang="lang" :dir="dir" :tabindex="tabindex" />
561+
`)
562+
563+
expect(code).matchSnapshot()
564+
expect(code).contains('_setDOMProp(n0, "id", _ctx.id, true)')
565+
expect(code).contains('_setDOMProp(n0, "title", _ctx.title, true)')
566+
expect(code).contains('_setDOMProp(n0, "lang", _ctx.lang, true)')
567+
expect(code).contains('_setDOMProp(n0, "dir", _ctx.dir, true)')
568+
expect(code).contains('_setDOMProp(n0, "tabindex", _ctx.tabindex, true)')
569+
})
570+
571+
test('SVG global attributes should set as dom prop', () => {
572+
const { code } = compileWithVBind(`
573+
<svg :id="id" :lang="lang" :tabindex="tabindex" />
574+
`)
575+
576+
expect(code).matchSnapshot()
577+
expect(code).contains('_setDOMProp(n0, "id", _ctx.id, true)')
578+
expect(code).contains('_setDOMProp(n0, "lang", _ctx.lang, true)')
579+
expect(code).contains('_setDOMProp(n0, "tabindex", _ctx.tabindex, true)')
580+
})
581+
582+
test('MathML global attributes should set as dom prop', () => {
583+
const { code } = compileWithVBind(`
584+
<math :autofucus :dir :displaystyle :mathcolor :tabindex/>
585+
`)
586+
587+
expect(code).matchSnapshot()
588+
expect(code).contains('_setDOMProp(n0, "autofucus", _ctx.autofucus, true)')
589+
expect(code).contains('_setDOMProp(n0, "dir", _ctx.dir, true)')
590+
expect(code).contains(
591+
'_setDOMProp(n0, "displaystyle", _ctx.displaystyle, true)',
592+
)
593+
expect(code).contains('_setDOMProp(n0, "mathcolor", _ctx.mathcolor, true)')
594+
expect(code).contains('_setDOMProp(n0, "tabindex", _ctx.tabindex, true)')
595+
})
596+
530597
test('number value', () => {
531598
const { code } = compileWithVBind(`<Comp :depth="0" />`)
532599
expect(code).matchSnapshot()

packages/compiler-vapor/src/generators/prop.ts

+29-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,17 @@ import {
2121
genCall,
2222
genMulti,
2323
} from './utils'
24-
import { toHandlerKey } from '@vue/shared'
24+
import {
25+
attributeCache,
26+
isHTMLGlobalAttr,
27+
isHTMLTag,
28+
isMathMLGlobalAttr,
29+
isMathMLTag,
30+
isSVGTag,
31+
isSvgGlobalAttr,
32+
shouldSetAsAttr,
33+
toHandlerKey,
34+
} from '@vue/shared'
2535

2636
// only the static key prop will reach here
2737
export function genSetProp(
@@ -31,9 +41,12 @@ export function genSetProp(
3141
const { vaporHelper } = context
3242
const {
3343
prop: { key, values, modifier },
44+
tag,
3445
} = oper
3546

3647
const keyName = key.content
48+
const tagName = tag.toUpperCase()
49+
const attrCacheKey = `${tagName}_${keyName}`
3750

3851
let helperName: VaporHelper
3952
let omitKey = false
@@ -45,6 +58,21 @@ export function genSetProp(
4558
omitKey = true
4659
} else if (modifier) {
4760
helperName = modifier === '.' ? 'setDOMProp' : 'setAttr'
61+
} else if (
62+
attributeCache[attrCacheKey] === undefined
63+
? (attributeCache[attrCacheKey] = shouldSetAsAttr(
64+
tag.toUpperCase(),
65+
keyName,
66+
))
67+
: attributeCache[attrCacheKey]
68+
) {
69+
helperName = 'setAttr'
70+
} else if (
71+
(isHTMLTag(tag) && isHTMLGlobalAttr(keyName)) ||
72+
(isSVGTag(tag) && isSvgGlobalAttr(keyName)) ||
73+
(isMathMLTag(tag) && isMathMLGlobalAttr(keyName))
74+
) {
75+
helperName = 'setDOMProp'
4876
} else {
4977
helperName = 'setDynamicProp'
5078
}

packages/compiler-vapor/src/ir/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ export interface SetPropIRNode extends BaseIRNode {
9494
element: number
9595
prop: IRProp
9696
root: boolean
97+
tag: string
9798
}
9899

99100
export interface SetDynamicPropsIRNode extends BaseIRNode {

packages/compiler-vapor/src/transforms/transformElement.ts

+1
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ function transformNativeElement(
210210
element: context.reference(),
211211
prop,
212212
root: singleRoot,
213+
tag,
213214
})
214215
}
215216
}

0 commit comments

Comments
 (0)