Skip to content

Commit

Permalink
perf(runtime-vapor): use setAttr or setDOMProp instead of `setDyn…
Browse files Browse the repository at this point in the history
…amicProp` when possible (#291)


Co-authored-by: Doctor Wu <doctorwu@moego.pet>
  • Loading branch information
edison1105 and Doctor-wu authored Nov 27, 2024
1 parent 9a2158d commit e61cedf
Show file tree
Hide file tree
Showing 9 changed files with 284 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
`;
exports[`compile > directives > v-pre > should not affect siblings after it 1`] = `
"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';
"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';
const t0 = _template("<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div>")
const t1 = _template("<div></div>")
Expand All @@ -162,7 +162,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
const n1 = _createComponent(_component_Comp)
const n2 = _createTextNode(() => [_ctx.bar])
_insert([n1, n2], n3)
_renderEffect(() => _setDynamicProp(n3, "id", _ctx.foo))
_renderEffect(() => _setDOMProp(n3, "id", _ctx.foo))
return [n0, n3]
}"
`;
Expand All @@ -177,7 +177,7 @@ export function render(_ctx) {
`;
exports[`compile > dynamic root nodes and interpolation 1`] = `
"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';
"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';
const t0 = _template("<button></button>")
_delegateEvents("click")
Expand All @@ -186,7 +186,7 @@ export function render(_ctx) {
_delegate(n0, "click", () => _ctx.handleClick)
_setInheritAttrs(["id"])
_renderEffect(() => _setText(n0, _ctx.count, "foo", _ctx.count, "foo", _ctx.count))
_renderEffect(() => _setDynamicProp(n0, "id", _ctx.count, true))
_renderEffect(() => _setDOMProp(n0, "id", _ctx.count, true))
return n0
}"
`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,100 @@ export function render(_ctx) {
}"
`;

exports[`compiler v-bind > HTML global attributes should set as dom prop 1`] = `
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>")
export function render(_ctx) {
const n0 = t0()
_setInheritAttrs(["id", "title", "lang", "dir", "tabindex"])
_renderEffect(() => _setDOMProp(n0, "id", _ctx.id, true))
_renderEffect(() => _setDOMProp(n0, "title", _ctx.title, true))
_renderEffect(() => _setDOMProp(n0, "lang", _ctx.lang, true))
_renderEffect(() => _setDOMProp(n0, "dir", _ctx.dir, true))
_renderEffect(() => _setDOMProp(n0, "tabindex", _ctx.tabindex, true))
return n0
}"
`;

exports[`compiler v-bind > MathML global attributes should set as dom prop 1`] = `
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
const t0 = _template("<math></math>")
export function render(_ctx) {
const n0 = t0()
_setInheritAttrs(["autofucus", "dir", "displaystyle", "mathcolor", "tabindex"])
_renderEffect(() => _setDOMProp(n0, "autofucus", _ctx.autofucus, true))
_renderEffect(() => _setDOMProp(n0, "dir", _ctx.dir, true))
_renderEffect(() => _setDOMProp(n0, "displaystyle", _ctx.displaystyle, true))
_renderEffect(() => _setDOMProp(n0, "mathcolor", _ctx.mathcolor, true))
_renderEffect(() => _setDOMProp(n0, "tabindex", _ctx.tabindex, true))
return n0
}"
`;

exports[`compiler v-bind > SVG global attributes should set as dom prop 1`] = `
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
const t0 = _template("<svg></svg>")
export function render(_ctx) {
const n0 = t0()
_setInheritAttrs(["id", "lang", "tabindex"])
_renderEffect(() => _setDOMProp(n0, "id", _ctx.id, true))
_renderEffect(() => _setDOMProp(n0, "lang", _ctx.lang, true))
_renderEffect(() => _setDOMProp(n0, "tabindex", _ctx.tabindex, true))
return n0
}"
`;

exports[`compiler v-bind > attributes must be set as attribute 1`] = `
"import { renderEffect as _renderEffect, setAttr as _setAttr, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>")
const t1 = _template("<input>")
const t2 = _template("<textarea></textarea>")
const t3 = _template("<img>")
const t4 = _template("<video></video>")
const t5 = _template("<canvas></canvas>")
const t6 = _template("<source>")
export function render(_ctx) {
const n0 = t0()
const n1 = t1()
const n2 = t2()
const n3 = t3()
const n4 = t4()
const n5 = t5()
const n6 = t6()
_renderEffect(() => _setAttr(n0, "spellcheck", _ctx.spellcheck))
_renderEffect(() => _setAttr(n0, "draggable", _ctx.draggable))
_renderEffect(() => _setAttr(n0, "translate", _ctx.translate))
_renderEffect(() => _setAttr(n0, "form", _ctx.form))
_renderEffect(() => _setAttr(n1, "list", _ctx.list))
_renderEffect(() => _setAttr(n2, "type", _ctx.type))
_renderEffect(() => {
_setAttr(n3, "width", _ctx.width)
_setAttr(n4, "width", _ctx.width)
_setAttr(n5, "width", _ctx.width)
_setAttr(n6, "width", _ctx.width)
})
_renderEffect(() => {
_setAttr(n3, "height", _ctx.height)
_setAttr(n4, "height", _ctx.height)
_setAttr(n5, "height", _ctx.height)
_setAttr(n6, "height", _ctx.height)
})
return [n0, n1, n2, n3, n4, n5, n6]
}"
`;
exports[`compiler v-bind > basic 1`] = `
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>")
export function render(_ctx) {
const n0 = t0()
_setInheritAttrs(["id"])
_renderEffect(() => _setDynamicProp(n0, "id", _ctx.id, true))
_renderEffect(() => _setDOMProp(n0, "id", _ctx.id, true))
return n0
}"
`;
Expand Down Expand Up @@ -170,13 +256,13 @@ export function render(_ctx) {
`;
exports[`compiler v-bind > no expression 1`] = `
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>")
export function render(_ctx) {
const n0 = t0()
_setInheritAttrs(["id"])
_renderEffect(() => _setDynamicProp(n0, "id", _ctx.id, true))
_renderEffect(() => _setDOMProp(n0, "id", _ctx.id, true))
return n0
}"
`;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`compiler: v-once > as root node 1`] = `
"import { setDynamicProp as _setDynamicProp, setInheritAttrs as _setInheritAttrs, template as _template } from 'vue/vapor';
"import { setDOMProp as _setDOMProp, setInheritAttrs as _setInheritAttrs, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>")
export function render(_ctx) {
const n0 = t0()
_setDynamicProp(n0, "id", _ctx.foo, true)
_setDOMProp(n0, "id", _ctx.foo, true)
_setInheritAttrs(["id"])
return n0
}"
Expand Down Expand Up @@ -52,13 +52,13 @@ export function render(_ctx) {
`;

exports[`compiler: v-once > on nested plain element 1`] = `
"import { setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
"import { setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
const t0 = _template("<div><div></div></div>")
export function render(_ctx) {
const n1 = t0()
const n0 = n1.firstChild
_setDynamicProp(n0, "id", _ctx.foo)
_setDOMProp(n0, "id", _ctx.foo)
return n1
}"
`;
Expand Down
71 changes: 69 additions & 2 deletions packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ describe('compiler v-bind', () => {
})

expect(code).matchSnapshot()
expect(code).contains('_setDynamicProp(n0, "id", _ctx.id, true)')
expect(code).contains('_setDOMProp(n0, "id", _ctx.id, true)')
})

test('no expression', () => {
Expand Down Expand Up @@ -104,7 +104,7 @@ describe('compiler v-bind', () => {
],
},
})
expect(code).contains('_setDynamicProp(n0, "id", _ctx.id, true)')
expect(code).contains('_setDOMProp(n0, "id", _ctx.id, true)')
})

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

test('attributes must be set as attribute', () => {
const { code } = compileWithVBind(`
<div :spellcheck :draggable :translate :form />
<input :list="list" />
<textarea :type="type" />
<img :width="width" :height="height"/>
<video :width="width" :height="height"/>
<canvas :width="width" :height="height"/>
<source :width="width" :height="height"/>
`)

expect(code).matchSnapshot()
expect(code).contains('_setAttr(n0, "spellcheck", _ctx.spellcheck)')
expect(code).contains('_setAttr(n0, "draggable", _ctx.draggable)')
expect(code).contains('_setAttr(n0, "translate", _ctx.translate)')
expect(code).contains('_setAttr(n0, "form", _ctx.form)')
expect(code).contains('_setAttr(n1, "list", _ctx.list)')
expect(code).contains('_setAttr(n2, "type", _ctx.type)')
expect(code).contains('_setAttr(n3, "width", _ctx.width)')
expect(code).contains('_setAttr(n3, "height", _ctx.height)')
expect(code).contains('_setAttr(n4, "width", _ctx.width)')
expect(code).contains('_setAttr(n4, "height", _ctx.height)')
expect(code).contains('_setAttr(n5, "width", _ctx.width)')
expect(code).contains('_setAttr(n5, "height", _ctx.height)')
expect(code).contains('_setAttr(n6, "width", _ctx.width)')
expect(code).contains('_setAttr(n6, "height", _ctx.height)')
})

test('HTML global attributes should set as dom prop', () => {
const { code } = compileWithVBind(`
<div :id="id" :title="title" :lang="lang" :dir="dir" :tabindex="tabindex" />
`)

expect(code).matchSnapshot()
expect(code).contains('_setDOMProp(n0, "id", _ctx.id, true)')
expect(code).contains('_setDOMProp(n0, "title", _ctx.title, true)')
expect(code).contains('_setDOMProp(n0, "lang", _ctx.lang, true)')
expect(code).contains('_setDOMProp(n0, "dir", _ctx.dir, true)')
expect(code).contains('_setDOMProp(n0, "tabindex", _ctx.tabindex, true)')
})

test('SVG global attributes should set as dom prop', () => {
const { code } = compileWithVBind(`
<svg :id="id" :lang="lang" :tabindex="tabindex" />
`)

expect(code).matchSnapshot()
expect(code).contains('_setDOMProp(n0, "id", _ctx.id, true)')
expect(code).contains('_setDOMProp(n0, "lang", _ctx.lang, true)')
expect(code).contains('_setDOMProp(n0, "tabindex", _ctx.tabindex, true)')
})

test('MathML global attributes should set as dom prop', () => {
const { code } = compileWithVBind(`
<math :autofucus :dir :displaystyle :mathcolor :tabindex/>
`)

expect(code).matchSnapshot()
expect(code).contains('_setDOMProp(n0, "autofucus", _ctx.autofucus, true)')
expect(code).contains('_setDOMProp(n0, "dir", _ctx.dir, true)')
expect(code).contains(
'_setDOMProp(n0, "displaystyle", _ctx.displaystyle, true)',
)
expect(code).contains('_setDOMProp(n0, "mathcolor", _ctx.mathcolor, true)')
expect(code).contains('_setDOMProp(n0, "tabindex", _ctx.tabindex, true)')
})

test('number value', () => {
const { code } = compileWithVBind(`<Comp :depth="0" />`)
expect(code).matchSnapshot()
Expand Down
30 changes: 29 additions & 1 deletion packages/compiler-vapor/src/generators/prop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,17 @@ import {
genCall,
genMulti,
} from './utils'
import { toHandlerKey } from '@vue/shared'
import {
attributeCache,
isHTMLGlobalAttr,
isHTMLTag,
isMathMLGlobalAttr,
isMathMLTag,
isSVGTag,
isSvgGlobalAttr,
shouldSetAsAttr,
toHandlerKey,
} from '@vue/shared'

// only the static key prop will reach here
export function genSetProp(
Expand All @@ -31,9 +41,12 @@ export function genSetProp(
const { vaporHelper } = context
const {
prop: { key, values, modifier },
tag,
} = oper

const keyName = key.content
const tagName = tag.toUpperCase()
const attrCacheKey = `${tagName}_${keyName}`

let helperName: VaporHelper
let omitKey = false
Expand All @@ -45,6 +58,21 @@ export function genSetProp(
omitKey = true
} else if (modifier) {
helperName = modifier === '.' ? 'setDOMProp' : 'setAttr'
} else if (
attributeCache[attrCacheKey] === undefined
? (attributeCache[attrCacheKey] = shouldSetAsAttr(
tag.toUpperCase(),
keyName,
))
: attributeCache[attrCacheKey]
) {
helperName = 'setAttr'
} else if (
(isHTMLTag(tag) && isHTMLGlobalAttr(keyName)) ||
(isSVGTag(tag) && isSvgGlobalAttr(keyName)) ||
(isMathMLTag(tag) && isMathMLGlobalAttr(keyName))
) {
helperName = 'setDOMProp'
} else {
helperName = 'setDynamicProp'
}
Expand Down
1 change: 1 addition & 0 deletions packages/compiler-vapor/src/ir/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export interface SetPropIRNode extends BaseIRNode {
element: number
prop: IRProp
root: boolean
tag: string
}

export interface SetDynamicPropsIRNode extends BaseIRNode {
Expand Down
1 change: 1 addition & 0 deletions packages/compiler-vapor/src/transforms/transformElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ function transformNativeElement(
element: context.reference(),
prop,
root: singleRoot,
tag,
})
}
}
Expand Down
Loading

0 comments on commit e61cedf

Please sign in to comment.