Skip to content

Commit

Permalink
refactor(compiler-vapor): cache multiple access to the same expression (
Browse files Browse the repository at this point in the history
  • Loading branch information
edison1105 authored Jan 8, 2025
1 parent 757b3df commit 58b4974
Show file tree
Hide file tree
Showing 9 changed files with 651 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,9 @@ export function render(_ctx) {
const n0 = t0()
_delegate(n0, "click", () => _ctx.handleClick)
_renderEffect(() => {
_setText(n0, _ctx.count, "foo", _ctx.count, "foo", _ctx.count)
_setProp(n0, "id", _ctx.count)
const _count = _ctx.count
_setText(n0, _count, "foo", _count, "foo", _count)
_setProp(n0, "id", _count)
})
return n0
}"
Expand All @@ -199,7 +200,10 @@ exports[`compile > expression parsing > interpolation 1`] = `
exports[`compile > expression parsing > v-bind 1`] = `
"
const n0 = t0()
_renderEffect(() => _setDynamicProps(n0, [{ [key.value+1]: _unref(foo)[key.value+1]() }], true))
_renderEffect(() => {
const _key = key.value
_setDynamicProps(n0, [{ [_key+1]: _unref(foo)[_key+1]() }], true)
})
return n0
"
`;
Expand Down
5 changes: 3 additions & 2 deletions packages/compiler-vapor/__tests__/compile.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,10 @@ describe('compile', () => {
},
})
expect(code).matchSnapshot()
expect(code).contains('key.value+1')
expect(code).contains('const _key = key.value')
expect(code).contains('_key+1')
expect(code).contains(
'_setDynamicProps(n0, [{ [key.value+1]: _unref(foo)[key.value+1]() }], true)',
'_setDynamicProps(n0, [{ [_key+1]: _unref(foo)[_key+1]() }], true)',
)
})

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,153 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`cache multiple access > dynamic key bindings with expressions 1`] = `
"import { setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue';
const t0 = _template("<div></div>", true)
export function render(_ctx) {
const n0 = t0()
_renderEffect(() => {
const _key = _ctx.key
_setDynamicProps(n0, [{ [_key+1]: _ctx.foo[_key+1]() }], true)
})
return n0
}"
`;

exports[`cache multiple access > dynamic property access 1`] = `
"import { setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
const t0 = _template("<div></div>", true)
export function render(_ctx) {
const n0 = t0()
_renderEffect(() => {
const _obj = _ctx.obj
_setProp(n0, "id", _obj[1][_ctx.baz] + _obj.bar)
})
return n0
}"
`;

exports[`cache multiple access > function calls with arguments 1`] = `
"import { setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
const t0 = _template("<div></div>")
export function render(_ctx) {
const n0 = t0()
const n1 = t0()
const n2 = t0()
_renderEffect(() => {
const _foo = _ctx.foo
const _bar = _ctx.bar
const _foo_bar_baz = _foo[_bar(_ctx.baz)]
_setProp(n0, "id", _foo_bar_baz)
_setProp(n1, "id", _foo_bar_baz)
_setProp(n2, "id", _bar() + _foo)
})
return [n0, n1, n2]
}"
`;

exports[`cache multiple access > not cache variable and member expression with the same name 1`] = `
"import { setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
const t0 = _template("<div></div>", true)
export function render(_ctx) {
const n0 = t0()
_renderEffect(() => _setProp(n0, "id", _ctx.bar + _ctx.obj.bar))
return n0
}"
`;

exports[`cache multiple access > object property chain access 1`] = `
"import { setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
const t0 = _template("<div></div>")
export function render(_ctx) {
const n0 = t0()
const n1 = t0()
_renderEffect(() => {
const _obj = _ctx.obj
const _obj_foo_baz_obj_bar = _obj['foo']['baz'] + _obj.bar
_setProp(n0, "id", _obj_foo_baz_obj_bar)
_setProp(n1, "id", _obj_foo_baz_obj_bar)
})
return [n0, n1]
}"
`;

exports[`cache multiple access > repeated expression in expressions 1`] = `
"import { setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
const t0 = _template("<div></div>")
export function render(_ctx) {
const n0 = t0()
const n1 = t0()
const n2 = t0()
_renderEffect(() => {
const _foo = _ctx.foo
const _foo_bar = _foo + _ctx.bar
_setProp(n0, "id", _foo_bar)
_setProp(n1, "id", _foo_bar)
_setProp(n2, "id", _foo + _foo_bar)
})
return [n0, n1, n2]
}"
`;

exports[`cache multiple access > repeated expressions 1`] = `
"import { setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
const t0 = _template("<div></div>")
export function render(_ctx) {
const n0 = t0()
const n1 = t0()
_renderEffect(() => {
const _foo_bar = _ctx.foo + _ctx.bar
_setProp(n0, "id", _foo_bar)
_setProp(n1, "id", _foo_bar)
})
return [n0, n1]
}"
`;

exports[`cache multiple access > repeated variable in expressions 1`] = `
"import { setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
const t0 = _template("<div></div>")
export function render(_ctx) {
const n0 = t0()
const n1 = t0()
_renderEffect(() => {
const _foo = _ctx.foo
_setProp(n0, "id", _foo + _foo + _ctx.bar)
_setProp(n1, "id", _foo)
})
return [n0, n1]
}"
`;

exports[`cache multiple access > repeated variables 1`] = `
"import { setClass as _setClass, renderEffect as _renderEffect, template as _template } from 'vue';
const t0 = _template("<div></div>")
export function render(_ctx) {
const n0 = t0()
const n1 = t0()
_renderEffect(() => {
const _foo = _ctx.foo
_setClass(n0, _foo)
_setClass(n1, _foo)
})
return [n0, n1]
}"
`;

exports[`compiler v-bind > .attr modifier 1`] = `
"import { setAttr as _setAttr, renderEffect as _renderEffect, template as _template } from 'vue';
const t0 = _template("<div></div>", true)
Expand Down Expand Up @@ -305,22 +453,24 @@ export function render(_ctx) {
const n5 = t5()
const n6 = t6()
_renderEffect(() => {
const _width = _ctx.width
const _height = _ctx.height
_setAttr(n0, "spellcheck", _ctx.spellcheck)
_setAttr(n0, "draggable", _ctx.draggable)
_setAttr(n0, "translate", _ctx.translate)
_setAttr(n0, "form", _ctx.form)
_setAttr(n1, "list", _ctx.list)
_setAttr(n2, "type", _ctx.type)
_setAttr(n3, "width", _ctx.width)
_setAttr(n4, "width", _ctx.width)
_setAttr(n5, "width", _ctx.width)
_setAttr(n6, "width", _ctx.width)
_setAttr(n3, "width", _width)
_setAttr(n4, "width", _width)
_setAttr(n5, "width", _width)
_setAttr(n6, "width", _width)
_setAttr(n3, "height", _ctx.height)
_setAttr(n4, "height", _ctx.height)
_setAttr(n5, "height", _ctx.height)
_setAttr(n6, "height", _ctx.height)
_setAttr(n3, "height", _height)
_setAttr(n4, "height", _height)
_setAttr(n5, "height", _height)
_setAttr(n6, "height", _height)
})
return [n0, n1, n2, n3, n4, n5, n6]
}"
Expand All @@ -343,7 +493,11 @@ const t0 = _template("<div></div>", true)
export function render(_ctx) {
const n0 = t0()
_renderEffect(() => _setDynamicProps(n0, [{ [_ctx.id]: _ctx.id, [_ctx.title]: _ctx.title }], true))
_renderEffect(() => {
const _id = _ctx.id
const _title = _ctx.title
_setDynamicProps(n0, [{ [_id]: _id, [_title]: _title }], true)
})
return n0
}"
`;
Expand All @@ -354,7 +508,10 @@ const t0 = _template("<div></div>", true)
export function render(_ctx) {
const n0 = t0()
_renderEffect(() => _setDynamicProps(n0, [{ [_ctx.id]: _ctx.id, foo: "bar", checked: "" }], true))
_renderEffect(() => {
const _id = _ctx.id
_setDynamicProps(n0, [{ [_id]: _id, foo: "bar", checked: "" }], true)
})
return n0
}"
`;
Expand Down
117 changes: 108 additions & 9 deletions packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ describe('compiler v-bind', () => {
],
})
expect(code).contains(
'_setDynamicProps(n0, [{ [_ctx.id]: _ctx.id, [_ctx.title]: _ctx.title }], true)',
'_setDynamicProps(n0, [{ [_id]: _id, [_title]: _title }], true)',
)
})

Expand Down Expand Up @@ -224,7 +224,7 @@ describe('compiler v-bind', () => {
],
})
expect(code).contains(
'_setDynamicProps(n0, [{ [_ctx.id]: _ctx.id, foo: "bar", checked: "" }], true)',
'_setDynamicProps(n0, [{ [_id]: _id, foo: "bar", checked: "" }], true)',
)
})

Expand Down Expand Up @@ -615,13 +615,13 @@ describe('compiler v-bind', () => {
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(n3, "width", _width)')
expect(code).contains('_setAttr(n3, "height", _height)')
expect(code).contains('_setAttr(n4, "width", _width)')
expect(code).contains('_setAttr(n4, "height", _height)')
expect(code).contains('_setAttr(n5, "width", _width)')
expect(code).contains('_setAttr(n5, "height", _height)')
expect(code).contains(' _setAttr(n6, "width", _width)')
})

test(':innerHTML', () => {
Expand Down Expand Up @@ -694,3 +694,102 @@ describe('compiler v-bind', () => {
expect(code).matchSnapshot()
})
})

describe('cache multiple access', () => {
test('repeated variables', () => {
const { code } = compileWithVBind(`
<div :class="foo"></div>
<div :class="foo"></div>
`)
expect(code).matchSnapshot()
expect(code).contains('const _foo = _ctx.foo')
expect(code).contains('setClass(n0, _foo)')
expect(code).contains('setClass(n1, _foo)')
})

test('repeated expressions', () => {
const { code } = compileWithVBind(`
<div :id="foo + bar"></div>
<div :id="foo + bar"></div>
`)
expect(code).matchSnapshot()
expect(code).contains('const _foo_bar = _ctx.foo + _ctx.bar')
expect(code).contains('_setProp(n0, "id", _foo_bar)')
expect(code).contains('_setProp(n1, "id", _foo_bar)')
})

test('repeated variable in expressions', () => {
const { code } = compileWithVBind(`
<div :id="foo + foo + bar"></div>
<div :id="foo"></div>
`)
expect(code).matchSnapshot()
expect(code).contains('const _foo = _ctx.foo')
expect(code).contains('_setProp(n0, "id", _foo + _foo + _ctx.bar)')
expect(code).contains('_setProp(n1, "id", _foo)')
})

test('repeated expression in expressions', () => {
const { code } = compileWithVBind(`
<div :id="foo + bar"></div>
<div :id="foo + bar"></div>
<div :id="foo + foo + bar"></div>
`)
expect(code).matchSnapshot()
expect(code).contains('const _foo_bar = _foo + _ctx.bar')
expect(code).contains('_setProp(n0, "id", _foo_bar)')
expect(code).contains('_setProp(n2, "id", _foo + _foo_bar)')
})

test('function calls with arguments', () => {
const { code } = compileWithVBind(`
<div :id="foo[bar(baz)]"></div>
<div :id="foo[bar(baz)]"></div>
<div :id="bar() + foo"></div>
`)
expect(code).matchSnapshot()
expect(code).contains('const _foo_bar_baz = _foo[_bar(_ctx.baz)]')
expect(code).contains('_setProp(n0, "id", _foo_bar_baz)')
expect(code).contains('_setProp(n1, "id", _foo_bar_baz)')
expect(code).contains('_setProp(n2, "id", _bar() + _foo)')
})

test('dynamic key bindings with expressions', () => {
const { code } = compileWithVBind(`
<div :[key+1]="foo[key+1]()" />
`)
expect(code).matchSnapshot()
expect(code).contains('const _key = _ctx.key')
expect(code).contains('[{ [_key+1]: _ctx.foo[_key+1]() }]')
})

test('object property chain access', () => {
const { code } = compileWithVBind(`
<div :id="obj['foo']['baz'] + obj.bar"></div>
<div :id="obj['foo']['baz'] + obj.bar"></div>
`)
expect(code).matchSnapshot()
expect(code).contains(
"const _obj_foo_baz_obj_bar = _obj['foo']['baz'] + _obj.bar",
)
expect(code).contains('_setProp(n0, "id", _obj_foo_baz_obj_bar)')
expect(code).contains('_setProp(n1, "id", _obj_foo_baz_obj_bar)')
})

test('dynamic property access', () => {
const { code } = compileWithVBind(`
<div :id="obj[1][baz] + obj.bar"></div>
`)
expect(code).matchSnapshot()
expect(code).contains('const _obj = _ctx.obj')
expect(code).contains('_setProp(n0, "id", _obj[1][_ctx.baz] + _obj.bar)')
})

test('not cache variable and member expression with the same name', () => {
const { code } = compileWithVBind(`
<div :id="bar + obj.bar"></div>
`)
expect(code).matchSnapshot()
expect(code).not.contains('const _bar = _ctx.bar')
})
})
Loading

0 comments on commit 58b4974

Please sign in to comment.