]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor: drop recordPropMetadata + merge renderEffect (#301)
authoredison <daiwei521@126.com>
Wed, 11 Dec 2024 06:02:34 +0000 (14:02 +0800)
committerGitHub <noreply@github.com>
Wed, 11 Dec 2024 06:02:34 +0000 (14:02 +0800)
30 files changed:
packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
packages/compiler-vapor/__tests__/compile.spec.ts
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vHtml.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vModel.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vOn.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vText.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts
packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
packages/compiler-vapor/src/generate.ts
packages/compiler-vapor/src/generators/expression.ts
packages/compiler-vapor/src/generators/html.ts
packages/compiler-vapor/src/generators/operation.ts
packages/compiler-vapor/src/generators/prop.ts
packages/compiler-vapor/src/generators/text.ts
packages/compiler-vapor/src/generators/utils.ts
packages/compiler-vapor/src/ir/index.ts
packages/compiler-vapor/src/transform.ts
packages/runtime-vapor/__tests__/apiSetupContext.spec.ts
packages/runtime-vapor/__tests__/dom/prop.spec.ts
packages/runtime-vapor/src/componentAttrs.ts
packages/runtime-vapor/src/componentFallback.ts
packages/runtime-vapor/src/componentMetadata.ts
packages/runtime-vapor/src/dom/prop.ts
packages/runtime-vapor/src/dom/style.ts

index 09e8c4ccaf614df826cef26dd280e630f27247bc..843b58e00f349f1a05edc7f9e59a52080a1c92ca 100644 (file)
@@ -1,12 +1,13 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`compile > bindings 1`] = `
-"import { renderEffect as _renderEffect, setText as _setText, template as _template } from 'vue/vapor';
+"import { setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx, $props, $emit, $attrs, $slots) {
   const n0 = t0()
-  _renderEffect(() => _setText(n0, "count is ", _ctx.count, "."))
+  let _count
+  _renderEffect(() => _count !== _ctx.count && _setText(n0, "count is ", (_count = _ctx.count), "."))
   return n0
 }"
 `;
@@ -151,7 +152,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, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
+"import { resolveComponent as _resolveComponent, createComponent as _createComponent, createTextNode as _createTextNode, insert as _insert, setDOMProp as _setDOMProp, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div>")
 const t1 = _template("<div></div>")
 
@@ -162,7 +163,8 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
   const n1 = _createComponent(_component_Comp)
   const n2 = _createTextNode(() => [_ctx.bar])
   _insert([n1, n2], n3)
-  _renderEffect(() => _setDOMProp(n3, "id", _ctx.foo))
+  let _foo
+  _renderEffect(() => _foo !== _ctx.foo && _setDOMProp(n3, "id", (_foo = _ctx.foo)))
   return [n0, n3]
 }"
 `;
@@ -177,7 +179,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, setDOMProp as _setDOMProp, delegateEvents as _delegateEvents, template as _template } from 'vue/vapor';
+"import { delegate as _delegate, setInheritAttrs as _setInheritAttrs, setText as _setText, setDOMProp as _setDOMProp, renderEffect as _renderEffect, delegateEvents as _delegateEvents, template as _template } from 'vue/vapor';
 const t0 = _template("<button></button>")
 _delegateEvents("click")
 
@@ -185,8 +187,14 @@ export function render(_ctx) {
   const n0 = t0()
   _delegate(n0, "click", () => _ctx.handleClick)
   _setInheritAttrs(["id"])
-  _renderEffect(() => _setText(n0, _ctx.count, "foo", _ctx.count, "foo", _ctx.count))
-  _renderEffect(() => _setDOMProp(n0, "id", _ctx.count))
+  let _count
+  _renderEffect(() => {
+    if(_count !== _ctx.count) {
+      _setText(n0, _ctx.count, "foo", _ctx.count, "foo", _ctx.count)
+      _setDOMProp(n0, "id", _ctx.count)
+      _count = _ctx.count
+    }
+  })
   return n0
 }"
 `;
@@ -202,7 +210,8 @@ exports[`compile > expression parsing > v-bind 1`] = `
 "((_ctx) => {
   const n0 = t0()
   _setInheritAttrs(true)
-  _renderEffect(() => _setDynamicProps(n0, [{ [key.value+1]: _unref(foo)[key.value+1]() }], true))
+  let _key_value, _foo, _key_value_foo
+  _renderEffect(() => (_key_value !== key.value || _foo !== _unref(foo)) && (_key_value_foo = _setDynamicProps(n0, _key_value_foo, [{ [key.value+1]: _unref(foo)[key.value+1]() }], true)))
   return n0
 })()"
 `;
index 2aeb777da1e84a8a8c2dc3ce048b078c3cfe7ee1..9b8770d59a6bc3caa890ccda26f75a58f24f8889 100644 (file)
@@ -194,7 +194,9 @@ describe('compile', () => {
       })
       expect(code).matchSnapshot()
       expect(code).contains('key.value+1')
-      expect(code).contains('_unref(foo)[key.value+1]()')
+      expect(code).contains(
+        '(_key_value !== key.value || _foo !== _unref(foo)) && (_key_value_foo = _setDynamicProps(n0, _key_value_foo, [{ [key.value+1]: _unref(foo)[key.value+1]() }], true))',
+      )
     })
 
     // TODO: add more test for expression parsing (v-on, v-slot, v-for)
index 951a534ec60476864afa8e943b6ccd522eabfade..db2dbaa575f557b3d74102724f24438d4919ac30 100644 (file)
@@ -1,7 +1,7 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`compiler: children transform > children & sibling references 1`] = `
-"import { next as _next, createTextNode as _createTextNode, insert as _insert, renderEffect as _renderEffect, setText as _setText, template as _template } from 'vue/vapor';
+"import { next as _next, createTextNode as _createTextNode, insert as _insert, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div><p></p> <!><p></p></div>")
 
 export function render(_ctx) {
@@ -11,8 +11,11 @@ export function render(_ctx) {
   const n2 = n3.nextSibling
   const n1 = _createTextNode(() => [_ctx.second, " ", _ctx.third, " "])
   _insert(n1, n4, n3)
-  _renderEffect(() => _setText(n0, _ctx.first))
-  _renderEffect(() => _setText(n2, _ctx.forth))
+  let _first, _forth
+  _renderEffect(() => {
+    _first !== _ctx.first && _setText(n0, (_first = _ctx.first))
+    _forth !== _ctx.forth && _setText(n2, (_forth = _ctx.forth))
+  })
   return n4
 }"
 `;
index 79b8fffbb1889042d49773713a487e83e488e7cd..2b5ab368026bf3d102f7a90d036f140b941c4de5 100644 (file)
@@ -297,13 +297,14 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: element transform > props merging: class 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setClass as _setClass, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setClass as _setClass, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["class"])
-  _renderEffect(() => _setClass(n0, ["foo", { bar: _ctx.isBar }], true))
+  let _isBar
+  _renderEffect(() => _isBar !== _ctx.isBar && _setClass(n0, ["foo", { bar: (_isBar = _ctx.isBar) }], true))
   return n0
 }"
 `;
@@ -326,7 +327,7 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: element transform > props merging: style 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setStyle as _setStyle, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setStyle as _setStyle, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
@@ -349,55 +350,59 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: element transform > v-bind="obj" 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(true)
-  _renderEffect(() => _setDynamicProps(n0, [_ctx.obj], true))
+  let _obj
+  _renderEffect(() => _obj !== _ctx.obj && (_obj = _setDynamicProps(n0, _obj, [_ctx.obj], true)))
   return n0
 }"
 `;
 
 exports[`compiler: element transform > v-bind="obj" after static prop 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(true)
-  _renderEffect(() => _setDynamicProps(n0, [{ id: "foo" }, _ctx.obj], true))
+  let _obj
+  _renderEffect(() => _obj !== _ctx.obj && (_obj = _setDynamicProps(n0, _obj, [{ id: "foo" }, _ctx.obj], true)))
   return n0
 }"
 `;
 
 exports[`compiler: element transform > v-bind="obj" before static prop 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(true)
-  _renderEffect(() => _setDynamicProps(n0, [_ctx.obj, { id: "foo" }], true))
+  let _obj
+  _renderEffect(() => _obj !== _ctx.obj && (_obj = _setDynamicProps(n0, _obj, [_ctx.obj, { id: "foo" }], true)))
   return n0
 }"
 `;
 
 exports[`compiler: element transform > v-bind="obj" between static props 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(true)
-  _renderEffect(() => _setDynamicProps(n0, [{ id: "foo" }, _ctx.obj, { class: "bar" }], true))
+  let _obj
+  _renderEffect(() => _obj !== _ctx.obj && (_obj = _setDynamicProps(n0, _obj, [{ id: "foo" }, _ctx.obj, { class: "bar" }], true)))
   return n0
 }"
 `;
 
 exports[`compiler: element transform > v-on="obj" 1`] = `
-"import { renderEffect as _renderEffect, setDynamicEvents as _setDynamicEvents, template as _template } from 'vue/vapor';
+"import { setDynamicEvents as _setDynamicEvents, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
index a600199256dbde699f38daf04becc9a251668811..efada2de917c3a7d3167fc84997d221bcf239f9e 100644 (file)
@@ -1,7 +1,7 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`compiler: template ref transform > dynamic ref 1`] = `
-"import { renderEffect as _renderEffect, setRef as _setRef, template as _template } from 'vue/vapor';
+"import { setRef as _setRef, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
index 4630eadfb55415b28f7f3aea9cdcf1fef49f59fd..f40057f3865fe31f95d2a78f181cac6dab029363 100644 (file)
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`compiler v-bind > .attr modifier 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setAttr as _setAttr, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setAttr as _setAttr, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["foo-bar"])
-  _renderEffect(() => _setAttr(n0, "foo-bar", _ctx.id))
+  let _id
+  _renderEffect(() => _id !== _ctx.id && _setAttr(n0, "foo-bar", (_id = _ctx.id)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .attr modifier w/ innerHTML 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setAttr as _setAttr, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setAttr as _setAttr, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["innerHTML"])
-  _renderEffect(() => _setAttr(n0, "innerHTML", _ctx.foo))
+  let _foo
+  _renderEffect(() => _foo !== _ctx.foo && _setAttr(n0, "innerHTML", (_foo = _ctx.foo)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .attr modifier w/ no expression 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setAttr as _setAttr, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setAttr as _setAttr, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["foo-bar"])
-  _renderEffect(() => _setAttr(n0, "foo-bar", _ctx.fooBar))
+  let _fooBar
+  _renderEffect(() => _fooBar !== _ctx.fooBar && _setAttr(n0, "foo-bar", (_fooBar = _ctx.fooBar)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .attr modifier w/ progress value 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setAttr as _setAttr, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setAttr as _setAttr, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<progress></progress>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["value"])
-  _renderEffect(() => _setAttr(n0, "value", _ctx.foo))
+  let _foo
+  _renderEffect(() => _foo !== _ctx.foo && _setAttr(n0, "value", (_foo = _ctx.foo)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .attr modifier w/ textContent 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setAttr as _setAttr, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setAttr as _setAttr, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["textContent"])
-  _renderEffect(() => _setAttr(n0, "textContent", _ctx.foo))
+  let _foo
+  _renderEffect(() => _foo !== _ctx.foo && _setAttr(n0, "textContent", (_foo = _ctx.foo)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .attr modifier w/ value 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setAttr as _setAttr, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setAttr as _setAttr, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["value"])
-  _renderEffect(() => _setAttr(n0, "value", _ctx.foo))
+  let _foo
+  _renderEffect(() => _foo !== _ctx.foo && _setAttr(n0, "value", (_foo = _ctx.foo)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .camel modifier 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDynamicProp as _setDynamicProp, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["fooBar"])
-  _renderEffect(() => _setDynamicProp(n0, "fooBar", _ctx.id))
+  let _id
+  _renderEffect(() => _id !== _ctx.id && (_id = _setDynamicProp(n0, "fooBar", _id, _ctx.id)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .camel modifier w/ dynamic arg 1`] = `
 "import { camelize as _camelize } from 'vue';
-import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor';
+import { setInheritAttrs as _setInheritAttrs, setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(true)
-  _renderEffect(() => _setDynamicProps(n0, [{ [_camelize(_ctx.foo)]: _ctx.id }], true))
+  let _foo, _id, _foo_id
+  _renderEffect(() => (_foo !== _ctx.foo || _id !== _ctx.id) && (_foo_id = _setDynamicProps(n0, _foo_id, [{ [_camelize(_ctx.foo)]: _ctx.id }], true)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .camel modifier w/ no expression 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDynamicProp as _setDynamicProp, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["fooBar"])
-  _renderEffect(() => _setDynamicProp(n0, "fooBar", _ctx.fooBar))
+  let _fooBar
+  _renderEffect(() => _fooBar !== _ctx.fooBar && (_fooBar = _setDynamicProp(n0, "fooBar", _fooBar, _ctx.fooBar)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .prop modifier (shorthand) 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDOMProp as _setDOMProp, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["fooBar"])
-  _renderEffect(() => _setDOMProp(n0, "fooBar", _ctx.id))
+  let _id
+  _renderEffect(() => _id !== _ctx.id && _setDOMProp(n0, "fooBar", (_id = _ctx.id)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .prop modifier (shorthand) w/ innerHTML 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setHtml as _setHtml, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setHtml as _setHtml, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["innerHTML"])
-  _renderEffect(() => _setHtml(n0, _ctx.foo))
+  let _foo
+  _renderEffect(() => _foo !== _ctx.foo && _setHtml(n0, (_foo = _ctx.foo)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .prop modifier (shorthand) w/ no expression 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDOMProp as _setDOMProp, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["fooBar"])
-  _renderEffect(() => _setDOMProp(n0, "fooBar", _ctx.fooBar))
+  let _fooBar
+  _renderEffect(() => _fooBar !== _ctx.fooBar && _setDOMProp(n0, "fooBar", (_fooBar = _ctx.fooBar)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .prop modifier (shorthand) w/ progress value 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDOMProp as _setDOMProp, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<progress></progress>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["value"])
-  _renderEffect(() => _setDOMProp(n0, "value", _ctx.foo))
+  let _foo
+  _renderEffect(() => _foo !== _ctx.foo && _setDOMProp(n0, "value", (_foo = _ctx.foo)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .prop modifier (shorthand) w/ textContent 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setText as _setText, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["textContent"])
-  _renderEffect(() => _setText(n0, _ctx.foo))
+  let _foo
+  _renderEffect(() => _foo !== _ctx.foo && _setText(n0, (_foo = _ctx.foo)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .prop modifier (shorthand) w/ value 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setValue as _setValue, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setValue as _setValue, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["value"])
-  _renderEffect(() => _setValue(n0, _ctx.foo))
+  let _foo
+  _renderEffect(() => _foo !== _ctx.foo && _setValue(n0, (_foo = _ctx.foo)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .prop modifier 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDOMProp as _setDOMProp, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["fooBar"])
-  _renderEffect(() => _setDOMProp(n0, "fooBar", _ctx.id))
+  let _id
+  _renderEffect(() => _id !== _ctx.id && _setDOMProp(n0, "fooBar", (_id = _ctx.id)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .prop modifier w/ dynamic arg 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(true)
-  _renderEffect(() => _setDynamicProps(n0, [{ ["." + _ctx.fooBar]: _ctx.id }], true))
+  let _fooBar, _id, _fooBar_id
+  _renderEffect(() => (_fooBar !== _ctx.fooBar || _id !== _ctx.id) && (_fooBar_id = _setDynamicProps(n0, _fooBar_id, [{ ["." + _ctx.fooBar]: _ctx.id }], true)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .prop modifier w/ innerHTML 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setHtml as _setHtml, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setHtml as _setHtml, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["innerHTML"])
-  _renderEffect(() => _setHtml(n0, _ctx.foo))
+  let _foo
+  _renderEffect(() => _foo !== _ctx.foo && _setHtml(n0, (_foo = _ctx.foo)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .prop modifier w/ no expression 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDOMProp as _setDOMProp, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["fooBar"])
-  _renderEffect(() => _setDOMProp(n0, "fooBar", _ctx.fooBar))
+  let _fooBar
+  _renderEffect(() => _fooBar !== _ctx.fooBar && _setDOMProp(n0, "fooBar", (_fooBar = _ctx.fooBar)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .prop modifier w/ progress value 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDOMProp as _setDOMProp, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<progress></progress>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["value"])
-  _renderEffect(() => _setDOMProp(n0, "value", _ctx.foo))
+  let _foo
+  _renderEffect(() => _foo !== _ctx.foo && _setDOMProp(n0, "value", (_foo = _ctx.foo)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .prop modifier w/ textContent 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setText as _setText, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["textContent"])
-  _renderEffect(() => _setText(n0, _ctx.foo))
+  let _foo
+  _renderEffect(() => _foo !== _ctx.foo && _setText(n0, (_foo = _ctx.foo)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .prop modifier w/ value 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setValue as _setValue, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setValue as _setValue, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["value"])
-  _renderEffect(() => _setValue(n0, _ctx.foo))
+  let _foo
+  _renderEffect(() => _foo !== _ctx.foo && _setValue(n0, (_foo = _ctx.foo)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > :innerHTML 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setHtml as _setHtml, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setHtml as _setHtml, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["innerHTML"])
-  _renderEffect(() => _setHtml(n0, _ctx.foo))
+  let _foo
+  _renderEffect(() => _foo !== _ctx.foo && _setHtml(n0, (_foo = _ctx.foo)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > :textContext 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setText as _setText, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["textContent"])
-  _renderEffect(() => _setText(n0, _ctx.foo))
+  let _foo
+  _renderEffect(() => _foo !== _ctx.foo && _setText(n0, (_foo = _ctx.foo)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > :value 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setValue as _setValue, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setValue as _setValue, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<input>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["value"])
-  _renderEffect(() => _setValue(n0, _ctx.foo))
+  let _foo
+  _renderEffect(() => _foo !== _ctx.foo && _setValue(n0, (_foo = _ctx.foo)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > :value w/ progress 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDynamicProp as _setDynamicProp, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<progress></progress>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["value"])
-  _renderEffect(() => _setDynamicProp(n0, "value", _ctx.foo))
+  let _foo
+  _renderEffect(() => _foo !== _ctx.foo && (_foo = _setDynamicProp(n0, "value", _foo, _ctx.foo)))
   return n0
 }"
 `;
 
 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';
+"import { setInheritAttrs as _setInheritAttrs, setDOMProp as _setDOMProp, renderEffect as _renderEffect, 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))
-  _renderEffect(() => _setDOMProp(n0, "title", _ctx.title))
-  _renderEffect(() => _setDOMProp(n0, "lang", _ctx.lang))
-  _renderEffect(() => _setDOMProp(n0, "dir", _ctx.dir))
-  _renderEffect(() => _setDOMProp(n0, "tabindex", _ctx.tabindex))
+  let _id, _title, _lang, _dir, _tabindex
+  _renderEffect(() => {
+    _id !== _ctx.id && _setDOMProp(n0, "id", (_id = _ctx.id))
+    _title !== _ctx.title && _setDOMProp(n0, "title", (_title = _ctx.title))
+    _lang !== _ctx.lang && _setDOMProp(n0, "lang", (_lang = _ctx.lang))
+    _dir !== _ctx.dir && _setDOMProp(n0, "dir", (_dir = _ctx.dir))
+    _tabindex !== _ctx.tabindex && _setDOMProp(n0, "tabindex", (_tabindex = _ctx.tabindex))
+  })
   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';
+"import { setInheritAttrs as _setInheritAttrs, setDOMProp as _setDOMProp, renderEffect as _renderEffect, 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))
-  _renderEffect(() => _setDOMProp(n0, "dir", _ctx.dir))
-  _renderEffect(() => _setDOMProp(n0, "displaystyle", _ctx.displaystyle))
-  _renderEffect(() => _setDOMProp(n0, "mathcolor", _ctx.mathcolor))
-  _renderEffect(() => _setDOMProp(n0, "tabindex", _ctx.tabindex))
+  let _autofucus, _dir, _displaystyle, _mathcolor, _tabindex
+  _renderEffect(() => {
+    _autofucus !== _ctx.autofucus && _setDOMProp(n0, "autofucus", (_autofucus = _ctx.autofucus))
+    _dir !== _ctx.dir && _setDOMProp(n0, "dir", (_dir = _ctx.dir))
+    _displaystyle !== _ctx.displaystyle && _setDOMProp(n0, "displaystyle", (_displaystyle = _ctx.displaystyle))
+    _mathcolor !== _ctx.mathcolor && _setDOMProp(n0, "mathcolor", (_mathcolor = _ctx.mathcolor))
+    _tabindex !== _ctx.tabindex && _setDOMProp(n0, "tabindex", (_tabindex = _ctx.tabindex))
+  })
   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';
+"import { setInheritAttrs as _setInheritAttrs, setDOMProp as _setDOMProp, renderEffect as _renderEffect, 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))
-  _renderEffect(() => _setDOMProp(n0, "lang", _ctx.lang))
-  _renderEffect(() => _setDOMProp(n0, "tabindex", _ctx.tabindex))
+  let _id, _lang, _tabindex
+  _renderEffect(() => {
+    _id !== _ctx.id && _setDOMProp(n0, "id", (_id = _ctx.id))
+    _lang !== _ctx.lang && _setDOMProp(n0, "lang", (_lang = _ctx.lang))
+    _tabindex !== _ctx.tabindex && _setDOMProp(n0, "tabindex", (_tabindex = _ctx.tabindex))
+  })
   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';
+"import { setAttr as _setAttr, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 const t1 = _template("<input>")
 const t2 = _template("<textarea></textarea>")
@@ -377,84 +412,106 @@ export function render(_ctx) {
   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)
-  })
+  let _spellcheck, _draggable, _translate, _form, _list, _type, _width, _height
   _renderEffect(() => {
-    _setAttr(n3, "height", _ctx.height)
-    _setAttr(n4, "height", _ctx.height)
-    _setAttr(n5, "height", _ctx.height)
-    _setAttr(n6, "height", _ctx.height)
+    _spellcheck !== _ctx.spellcheck && _setAttr(n0, "spellcheck", (_spellcheck = _ctx.spellcheck))
+    _draggable !== _ctx.draggable && _setAttr(n0, "draggable", (_draggable = _ctx.draggable))
+    _translate !== _ctx.translate && _setAttr(n0, "translate", (_translate = _ctx.translate))
+    _form !== _ctx.form && _setAttr(n0, "form", (_form = _ctx.form))
+    _list !== _ctx.list && _setAttr(n1, "list", (_list = _ctx.list))
+    _type !== _ctx.type && _setAttr(n2, "type", (_type = _ctx.type))
+    if(_width !== _ctx.width) {
+      _setAttr(n3, "width", _ctx.width)
+      _setAttr(n4, "width", _ctx.width)
+      _setAttr(n5, "width", _ctx.width)
+      _setAttr(n6, "width", _ctx.width)
+      _width = _ctx.width
+    }
+    if(_height !== _ctx.height) {
+      _setAttr(n3, "height", _ctx.height)
+      _setAttr(n4, "height", _ctx.height)
+      _setAttr(n5, "height", _ctx.height)
+      _setAttr(n6, "height", _ctx.height)
+      _height = _ctx.height
+    }
   })
   return [n0, n1, n2, n3, n4, n5, n6]
 }"
 `;
 
 exports[`compiler v-bind > basic 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDOMProp as _setDOMProp, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["id"])
-  _renderEffect(() => _setDOMProp(n0, "id", _ctx.id))
+  let _id
+  _renderEffect(() => _id !== _ctx.id && _setDOMProp(n0, "id", (_id = _ctx.id)))
   return n0
 }"
 `;
 
+exports[`compiler v-bind > bind member expression 1`] = `
+"import { setDOMProp as _setDOMProp, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
+const t0 = _template("<div></div>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  let _obj
+  _renderEffect(() => _obj !== _ctx.obj.count.bar && _setDOMProp(n0, "id", (_obj = _ctx.obj.count.bar)))
+  return [n0, n1]
+}"
+`;
+
 exports[`compiler v-bind > dynamic arg 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(true)
-  _renderEffect(() => _setDynamicProps(n0, [{ [_ctx.id]: _ctx.id, [_ctx.title]: _ctx.title }], true))
+  let _id, _title, _id_title
+  _renderEffect(() => (_id !== _ctx.id || _title !== _ctx.title) && (_id_title = _setDynamicProps(n0, _id_title, [{ [_ctx.id]: _ctx.id, [_ctx.title]: _ctx.title }], true)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > dynamic arg w/ static attribute 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(true)
-  _renderEffect(() => _setDynamicProps(n0, [{ [_ctx.id]: _ctx.id, foo: "bar", checked: "" }], true))
+  let _id
+  _renderEffect(() => _id !== _ctx.id && (_id = _setDynamicProps(n0, _id, [{ [_ctx.id]: _ctx.id, foo: "bar", checked: "" }], true)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > no expression (shorthand) 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDynamicProp as _setDynamicProp, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["camel-case"])
-  _renderEffect(() => _setDynamicProp(n0, "camel-case", _ctx.camelCase))
+  let _camelCase
+  _renderEffect(() => _camelCase !== _ctx.camelCase && (_camelCase = _setDynamicProp(n0, "camel-case", _camelCase, _ctx.camelCase)))
   return n0
 }"
 `;
 
 exports[`compiler v-bind > no expression 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDOMProp as _setDOMProp, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["id"])
-  _renderEffect(() => _setDOMProp(n0, "id", _ctx.id))
+  let _id
+  _renderEffect(() => _id !== _ctx.id && _setDOMProp(n0, "id", (_id = _ctx.id)))
   return n0
 }"
 `;
index a82221728a3aeddfb6492d86a8d7ba8ae72ff351..580b41046a7d32b811ee5fffa771363239a352bf 100644 (file)
@@ -1,7 +1,7 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`compiler: v-for > array de-structured value 1`] = `
-"import { renderEffect as _renderEffect, setText as _setText, withDestructure as _withDestructure, createFor as _createFor, template as _template } from 'vue/vapor';
+"import { setText as _setText, renderEffect as _renderEffect, withDestructure as _withDestructure, createFor as _createFor, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
@@ -15,7 +15,7 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-for > basic v-for 1`] = `
-"import { delegate as _delegate, renderEffect as _renderEffect, setText as _setText, createFor as _createFor, delegateEvents as _delegateEvents, template as _template } from 'vue/vapor';
+"import { delegate as _delegate, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, delegateEvents as _delegateEvents, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 _delegateEvents("click")
 
@@ -31,7 +31,7 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-for > function params w/ prefixIdentifiers: false 1`] = `
-"import { renderEffect as _renderEffect, setText as _setText, createFor as _createFor, template as _template } from 'vue/vapor';
+"import { setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
@@ -45,15 +45,17 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-for > multi effect 1`] = `
-"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, createFor as _createFor, template as _template } from 'vue/vapor';
+"import { setInheritAttrs as _setInheritAttrs, setDynamicProp as _setDynamicProp, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = _createFor(() => (_ctx.items), (_ctx0) => {
     const n2 = t0()
     _setInheritAttrs(["item", "index"])
-    _renderEffect(() => _setDynamicProp(n2, "item", _ctx0[0].value))
-    _renderEffect(() => _setDynamicProp(n2, "index", _ctx0[1].value))
+    _renderEffect(() => {
+      _setDynamicProp(n2, "item", _ctx0[0].value)
+      _setDynamicProp(n2, "index", _ctx0[1].value)
+    })
     return n2
   })
   return n0
@@ -61,7 +63,7 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-for > nested v-for 1`] = `
-"import { renderEffect as _renderEffect, setText as _setText, createFor as _createFor, insert as _insert, template as _template } from 'vue/vapor';
+"import { setText as _setText, renderEffect as _renderEffect, createFor as _createFor, insert as _insert, template as _template } from 'vue/vapor';
 const t0 = _template("<span></span>")
 const t1 = _template("<div></div>")
 
@@ -81,7 +83,7 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-for > object de-structured value 1`] = `
-"import { renderEffect as _renderEffect, setText as _setText, withDestructure as _withDestructure, createFor as _createFor, template as _template } from 'vue/vapor';
+"import { setText as _setText, renderEffect as _renderEffect, withDestructure as _withDestructure, createFor as _createFor, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
@@ -95,7 +97,7 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-for > v-for aliases w/ complex expressions 1`] = `
-"import { renderEffect as _renderEffect, setText as _setText, withDestructure as _withDestructure, createFor as _createFor, template as _template } from 'vue/vapor';
+"import { setText as _setText, renderEffect as _renderEffect, withDestructure as _withDestructure, createFor as _createFor, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
index a5ee792e2d1b7d63e9144b2e0d7b7964a20fcb7e..34b621bc6184786eba1078ec6b17bfe83758d55d 100644 (file)
@@ -1,23 +1,25 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`v-html > should convert v-html to innerHTML 1`] = `
-"import { renderEffect as _renderEffect, setHtml as _setHtml, template as _template } from 'vue/vapor';
+"import { setHtml as _setHtml, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx, $props, $emit, $attrs, $slots) {
   const n0 = t0()
-  _renderEffect(() => _setHtml(n0, _ctx.code))
+  let _code
+  _renderEffect(() => _code !== _ctx.code && _setHtml(n0, (_code = _ctx.code)))
   return n0
 }"
 `;
 
 exports[`v-html > should raise error and ignore children when v-html is present 1`] = `
-"import { renderEffect as _renderEffect, setHtml as _setHtml, template as _template } from 'vue/vapor';
+"import { setHtml as _setHtml, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
-  _renderEffect(() => _setHtml(n0, _ctx.test))
+  let _test
+  _renderEffect(() => _test !== _ctx.test && _setHtml(n0, (_test = _ctx.test)))
   return n0
 }"
 `;
index 3d92452a165df4e67c07d9629b1db014e332fb7d..42cb0a4d26b5c5ac801c47b1b1be82217e51a998 100644 (file)
@@ -1,13 +1,14 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`compiler: v-if > basic v-if 1`] = `
-"import { renderEffect as _renderEffect, setText as _setText, createIf as _createIf, template as _template } from 'vue/vapor';
+"import { setText as _setText, renderEffect as _renderEffect, createIf as _createIf, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = _createIf(() => (_ctx.ok), () => {
     const n2 = t0()
-    _renderEffect(() => _setText(n2, _ctx.msg))
+    let _msg
+    _renderEffect(() => _msg !== _ctx.msg && _setText(n2, (_msg = _ctx.msg)))
     return n2
   })
   return n0
@@ -15,7 +16,7 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-if > comment between branches 1`] = `
-"import { createIf as _createIf, renderEffect as _renderEffect, setText as _setText, template as _template } from 'vue/vapor';
+"import { createIf as _createIf, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 const t1 = _template("<!--foo-->")
 const t2 = _template("<p></p>")
@@ -37,7 +38,8 @@ export function render(_ctx) {
     const n11 = t4()
     return [n10, n11]
   }))
-  _renderEffect(() => _setText(n13, _ctx.text))
+  let _text
+  _renderEffect(() => _text !== _ctx.text && _setText(n13, (_text = _ctx.text)))
   return [n0, n13]
 }"
 `;
@@ -60,7 +62,7 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-if > template v-if 1`] = `
-"import { renderEffect as _renderEffect, setText as _setText, createIf as _createIf, template as _template } from 'vue/vapor';
+"import { setText as _setText, renderEffect as _renderEffect, createIf as _createIf, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 const t1 = _template("hello")
 const t2 = _template("<p></p>")
@@ -70,7 +72,8 @@ export function render(_ctx) {
     const n2 = t0()
     const n3 = t1()
     const n4 = t2()
-    _renderEffect(() => _setText(n4, _ctx.msg))
+    let _msg
+    _renderEffect(() => _msg !== _ctx.msg && _setText(n4, (_msg = _ctx.msg)))
     return [n2, n3, n4]
   })
   return n0
index 04ceb3c5ac1d484cf225200760b9dc04cc17f057..0b3851662131c14c0bd40dbc76b35edea76afe26 100644 (file)
@@ -246,7 +246,7 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: vModel transform > should support w/ dynamic v-bind 1`] = `
-"import { vModelDynamic as _vModelDynamic, withDirectives as _withDirectives, delegate as _delegate, setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from 'vue/vapor';
+"import { vModelDynamic as _vModelDynamic, withDirectives as _withDirectives, delegate as _delegate, setInheritAttrs as _setInheritAttrs, setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<input>")
 
 export function render(_ctx) {
@@ -254,7 +254,8 @@ export function render(_ctx) {
   _withDirectives(n0, [[_vModelDynamic, () => _ctx.model]])
   _delegate(n0, "update:modelValue", () => $event => (_ctx.model = $event))
   _setInheritAttrs(true)
-  _renderEffect(() => _setDynamicProps(n0, [_ctx.obj], true))
+  let _obj
+  _renderEffect(() => _obj !== _ctx.obj && (_obj = _setDynamicProps(n0, _obj, [_ctx.obj], true)))
   return n0
 }"
 `;
index cbd195b83e3fe52873138888852b01735abcfbfe..8657e0611f98b0a658d9adb06afd94e1e36d12e1 100644 (file)
@@ -13,12 +13,13 @@ export function render(_ctx) {
 `;
 
 exports[`v-on > dynamic arg 1`] = `
-"import { renderEffect as _renderEffect, on as _on, template as _template } from 'vue/vapor';
+"import { on as _on, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _renderEffect(() => {
+    
     _on(n0, _ctx.event, () => _ctx.handler, {
       effect: true
     })
@@ -28,12 +29,13 @@ export function render(_ctx) {
 `;
 
 exports[`v-on > dynamic arg with complex exp prefixing 1`] = `
-"import { renderEffect as _renderEffect, on as _on, template as _template } from 'vue/vapor';
+"import { on as _on, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _renderEffect(() => {
+    
     _on(n0, _ctx.event(_ctx.foo), () => _ctx.handler, {
       effect: true
     })
@@ -43,12 +45,13 @@ export function render(_ctx) {
 `;
 
 exports[`v-on > dynamic arg with prefixing 1`] = `
-"import { renderEffect as _renderEffect, on as _on, template as _template } from 'vue/vapor';
+"import { on as _on, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _renderEffect(() => {
+    
     _on(n0, _ctx.event, () => _ctx.handler, {
       effect: true
     })
@@ -372,12 +375,13 @@ export function render(_ctx) {
 `;
 
 exports[`v-on > should transform click.middle 2`] = `
-"import { renderEffect as _renderEffect, on as _on, template as _template } from 'vue/vapor';
+"import { on as _on, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _renderEffect(() => {
+    
     _on(n0, (_ctx.event) === "click" ? "mouseup" : (_ctx.event), () => _ctx.test, {
       modifiers: ["middle"], 
       effect: true
@@ -402,12 +406,13 @@ export function render(_ctx) {
 `;
 
 exports[`v-on > should transform click.right 2`] = `
-"import { renderEffect as _renderEffect, on as _on, template as _template } from 'vue/vapor';
+"import { on as _on, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _renderEffect(() => {
+    
     _on(n0, (_ctx.event) === "click" ? "contextmenu" : (_ctx.event), () => _ctx.test, {
       modifiers: ["right"], 
       keys: ["right"], 
@@ -431,12 +436,13 @@ export function render(_ctx) {
 `;
 
 exports[`v-on > should wrap both for dynamic key event w/ left/right modifiers 1`] = `
-"import { renderEffect as _renderEffect, on as _on, template as _template } from 'vue/vapor';
+"import { on as _on, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _renderEffect(() => {
+    
     _on(n0, _ctx.e, () => _ctx.test, {
       modifiers: ["left"], 
       keys: ["left"], 
index 622966ba8d9f010a70f654605d4de6908a17cd27..1b98a024ce91f327b2b1a9f8512487738b04efed 100644 (file)
@@ -1,23 +1,25 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`v-text > should convert v-text to textContent 1`] = `
-"import { renderEffect as _renderEffect, setText as _setText, template as _template } from 'vue/vapor';
+"import { setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx, $props, $emit, $attrs, $slots) {
   const n0 = t0()
-  _renderEffect(() => _setText(n0, _ctx.str))
+  let _str
+  _renderEffect(() => _str !== _ctx.str && _setText(n0, (_str = _ctx.str)))
   return n0
 }"
 `;
 
 exports[`v-text > should raise error and ignore children when v-text is present 1`] = `
-"import { renderEffect as _renderEffect, setText as _setText, template as _template } from 'vue/vapor';
+"import { setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
-  _renderEffect(() => _setText(n0, _ctx.test))
+  let _test
+  _renderEffect(() => _test !== _ctx.test && _setText(n0, (_test = _ctx.test)))
   return n0
 }"
 `;
index 2b5f894645612113fa4ce9e1b02995006821512f..631beac52a01df6e62c3aff26d0f4ddd8cdda235 100644 (file)
@@ -588,7 +588,9 @@ describe('compiler: element transform', () => {
         ],
       },
     ])
-    expect(code).contains('_setDynamicProps(n0, [_ctx.obj], true)')
+    expect(code).contains(
+      '_obj !== _ctx.obj && (_obj = _setDynamicProps(n0, _obj, [_ctx.obj], true))',
+    )
   })
 
   test('v-bind="obj" after static prop', () => {
@@ -625,7 +627,7 @@ describe('compiler: element transform', () => {
       },
     ])
     expect(code).contains(
-      '_setDynamicProps(n0, [{ id: "foo" }, _ctx.obj], true)',
+      '_obj !== _ctx.obj && (_obj = _setDynamicProps(n0, _obj, [{ id: "foo" }, _ctx.obj], true))',
     )
   })
 
@@ -653,7 +655,7 @@ describe('compiler: element transform', () => {
       },
     ])
     expect(code).contains(
-      '_setDynamicProps(n0, [_ctx.obj, { id: "foo" }], true)',
+      '_obj !== _ctx.obj && (_obj = _setDynamicProps(n0, _obj, [_ctx.obj, { id: "foo" }], true))',
     )
   })
 
@@ -682,7 +684,7 @@ describe('compiler: element transform', () => {
       },
     ])
     expect(code).contains(
-      '_setDynamicProps(n0, [{ id: "foo" }, _ctx.obj, { class: "bar" }], true)',
+      '_obj !== _ctx.obj && (_obj = _setDynamicProps(n0, _obj, [{ id: "foo" }, _ctx.obj, { class: "bar" }], true))',
     )
   })
 
index efaa9257ceb068d22e3aa6f780692e7ab5a1d9be..87c7752aa3ce528855cb99a69e9b4d78bb4c0363 100644 (file)
@@ -74,7 +74,9 @@ describe('compiler v-bind', () => {
     })
 
     expect(code).matchSnapshot()
-    expect(code).contains('_setDOMProp(n0, "id", _ctx.id)')
+    expect(code).contains(
+      '_id !== _ctx.id && _setDOMProp(n0, "id", (_id = _ctx.id))',
+    )
   })
 
   test('no expression', () => {
@@ -104,7 +106,9 @@ describe('compiler v-bind', () => {
         ],
       },
     })
-    expect(code).contains('_setDOMProp(n0, "id", _ctx.id)')
+    expect(code).contains(
+      '_id !== _ctx.id && _setDOMProp(n0, "id", (_id = _ctx.id))',
+    )
   })
 
   test('no expression (shorthand)', () => {
@@ -126,7 +130,9 @@ describe('compiler v-bind', () => {
         ],
       },
     })
-    expect(code).contains('_setDynamicProp(n0, "camel-case", _ctx.camelCase)')
+    expect(code).contains(
+      '_camelCase !== _ctx.camelCase && (_camelCase = _setDynamicProp(n0, "camel-case", _camelCase, _ctx.camelCase))',
+    )
   })
 
   test('dynamic arg', () => {
@@ -171,7 +177,7 @@ describe('compiler v-bind', () => {
       ],
     })
     expect(code).contains(
-      '_setDynamicProps(n0, [{ [_ctx.id]: _ctx.id, [_ctx.title]: _ctx.title }], true)',
+      '(_id !== _ctx.id || _title !== _ctx.title) && (_id_title = _setDynamicProps(n0, _id_title, [{ [_ctx.id]: _ctx.id, [_ctx.title]: _ctx.title }], true))',
     )
   })
 
@@ -224,7 +230,7 @@ describe('compiler v-bind', () => {
       ],
     })
     expect(code).contains(
-      '_setDynamicProps(n0, [{ [_ctx.id]: _ctx.id, foo: "bar", checked: "" }], true)',
+      '_id !== _ctx.id && (_id = _setDynamicProps(n0, _id, [{ [_ctx.id]: _ctx.id, foo: "bar", checked: "" }], true))',
     )
   })
 
@@ -286,7 +292,9 @@ describe('compiler v-bind', () => {
     })
 
     expect(code).matchSnapshot()
-    expect(code).contains('_setDynamicProp(n0, "fooBar", _ctx.id)')
+    expect(code).contains(
+      '_id !== _ctx.id && (_id = _setDynamicProp(n0, "fooBar", _id, _ctx.id))',
+    )
   })
 
   test('.camel modifier w/ no expression', () => {
@@ -310,7 +318,9 @@ describe('compiler v-bind', () => {
       },
     })
     expect(code).contains('renderEffect')
-    expect(code).contains('_setDynamicProp(n0, "fooBar", _ctx.fooBar)')
+    expect(code).contains(
+      '_fooBar !== _ctx.fooBar && (_fooBar = _setDynamicProp(n0, "fooBar", _fooBar, _ctx.fooBar))',
+    )
   })
 
   test('.camel modifier w/ dynamic arg', () => {
@@ -341,7 +351,7 @@ describe('compiler v-bind', () => {
     expect(code).matchSnapshot()
     expect(code).contains('renderEffect')
     expect(code).contains(
-      `_setDynamicProps(n0, [{ [_camelize(_ctx.foo)]: _ctx.id }], true)`,
+      `(_foo !== _ctx.foo || _id !== _ctx.id) && (_foo_id = _setDynamicProps(n0, _foo_id, [{ [_camelize(_ctx.foo)]: _ctx.id }], true))`,
     )
   })
 
@@ -368,7 +378,9 @@ describe('compiler v-bind', () => {
       },
     })
     expect(code).contains('renderEffect')
-    expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.id)')
+    expect(code).contains(
+      '_id !== _ctx.id && _setDOMProp(n0, "fooBar", (_id = _ctx.id))',
+    )
   })
 
   test('.prop modifier w/ no expression', () => {
@@ -392,7 +404,9 @@ describe('compiler v-bind', () => {
       },
     })
     expect(code).contains('renderEffect')
-    expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.fooBar)')
+    expect(code).contains(
+      '_fooBar !== _ctx.fooBar && _setDOMProp(n0, "fooBar", (_fooBar = _ctx.fooBar))',
+    )
   })
 
   test('.prop modifier w/ dynamic arg', () => {
@@ -422,7 +436,7 @@ describe('compiler v-bind', () => {
     })
     expect(code).contains('renderEffect')
     expect(code).contains(
-      `_setDynamicProps(n0, [{ ["." + _ctx.fooBar]: _ctx.id }], true)`,
+      `(_fooBar !== _ctx.fooBar || _id !== _ctx.id) && (_fooBar_id = _setDynamicProps(n0, _fooBar_id, [{ ["." + _ctx.fooBar]: _ctx.id }], true))`,
     )
   })
 
@@ -449,7 +463,9 @@ describe('compiler v-bind', () => {
       },
     })
     expect(code).contains('renderEffect')
-    expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.id)')
+    expect(code).contains(
+      '_id !== _ctx.id && _setDOMProp(n0, "fooBar", (_id = _ctx.id))',
+    )
   })
 
   test('.prop modifier (shorthand) w/ no expression', () => {
@@ -473,55 +489,73 @@ describe('compiler v-bind', () => {
       },
     })
     expect(code).contains('renderEffect')
-    expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.fooBar)')
+    expect(code).contains(
+      '_fooBar !== _ctx.fooBar && _setDOMProp(n0, "fooBar", (_fooBar = _ctx.fooBar))',
+    )
   })
 
   test('.prop modifier w/ innerHTML', () => {
     const { code } = compileWithVBind(`<div :innerHTML.prop="foo" />`)
     expect(code).matchSnapshot()
-    expect(code).contains('_setHtml(n0, _ctx.foo)')
+    expect(code).contains(
+      '_foo !== _ctx.foo && _setHtml(n0, (_foo = _ctx.foo))',
+    )
   })
 
   test('.prop modifier (shorthand) w/ innerHTML', () => {
     const { code } = compileWithVBind(`<div .innerHTML="foo" />`)
     expect(code).matchSnapshot()
-    expect(code).contains('_setHtml(n0, _ctx.foo)')
+    expect(code).contains(
+      '_foo !== _ctx.foo && _setHtml(n0, (_foo = _ctx.foo))',
+    )
   })
 
   test('.prop modifier w/ textContent', () => {
     const { code } = compileWithVBind(`<div :textContent.prop="foo" />`)
     expect(code).matchSnapshot()
-    expect(code).contains('_setText(n0, _ctx.foo)')
+    expect(code).contains(
+      '_foo !== _ctx.foo && _setText(n0, (_foo = _ctx.foo))',
+    )
   })
 
   test('.prop modifier (shorthand) w/ textContent', () => {
     const { code } = compileWithVBind(`<div .textContent="foo" />`)
     expect(code).matchSnapshot()
-    expect(code).contains('_setText(n0, _ctx.foo)')
+    expect(code).contains(
+      '_foo !== _ctx.foo && _setText(n0, (_foo = _ctx.foo))',
+    )
   })
 
   test('.prop modifier w/ value', () => {
     const { code } = compileWithVBind(`<div :value.prop="foo" />`)
     expect(code).matchSnapshot()
-    expect(code).contains('_setValue(n0, _ctx.foo)')
+    expect(code).contains(
+      '_foo !== _ctx.foo && _setValue(n0, (_foo = _ctx.foo))',
+    )
   })
 
   test('.prop modifier (shorthand) w/ value', () => {
     const { code } = compileWithVBind(`<div .value="foo" />`)
     expect(code).matchSnapshot()
-    expect(code).contains('_setValue(n0, _ctx.foo)')
+    expect(code).contains(
+      '_foo !== _ctx.foo && _setValue(n0, (_foo = _ctx.foo))',
+    )
   })
 
   test('.prop modifier w/ progress value', () => {
     const { code } = compileWithVBind(`<progress :value.prop="foo" />`)
     expect(code).matchSnapshot()
-    expect(code).contains('_setDOMProp(n0, "value", _ctx.foo)')
+    expect(code).contains(
+      '_foo !== _ctx.foo && _setDOMProp(n0, "value", (_foo = _ctx.foo))',
+    )
   })
 
   test('.prop modifier (shorthand) w/ progress value', () => {
     const { code } = compileWithVBind(`<progress .value="foo" />`)
     expect(code).matchSnapshot()
-    expect(code).contains('_setDOMProp(n0, "value", _ctx.foo)')
+    expect(code).contains(
+      '_foo !== _ctx.foo && _setDOMProp(n0, "value", (_foo = _ctx.foo))',
+    )
   })
 
   test('.attr modifier', () => {
@@ -545,7 +579,9 @@ describe('compiler v-bind', () => {
       },
     })
     expect(code).contains('renderEffect')
-    expect(code).contains('_setAttr(n0, "foo-bar", _ctx.id)')
+    expect(code).contains(
+      '_id !== _ctx.id && _setAttr(n0, "foo-bar", (_id = _ctx.id))',
+    )
   })
 
   test('.attr modifier w/ no expression', () => {
@@ -570,31 +606,41 @@ describe('compiler v-bind', () => {
     })
 
     expect(code).contains('renderEffect')
-    expect(code).contains('_setAttr(n0, "foo-bar", _ctx.fooBar)')
+    expect(code).contains(
+      '_fooBar !== _ctx.fooBar && _setAttr(n0, "foo-bar", (_fooBar = _ctx.fooBar))',
+    )
   })
 
   test('.attr modifier w/ innerHTML', () => {
     const { code } = compileWithVBind(`<div :innerHTML.attr="foo" />`)
     expect(code).matchSnapshot()
-    expect(code).contains('_setAttr(n0, "innerHTML", _ctx.foo)')
+    expect(code).contains(
+      '_foo !== _ctx.foo && _setAttr(n0, "innerHTML", (_foo = _ctx.foo))',
+    )
   })
 
   test('.attr modifier w/ textContent', () => {
     const { code } = compileWithVBind(`<div :textContent.attr="foo" />`)
     expect(code).matchSnapshot()
-    expect(code).contains('_setAttr(n0, "textContent", _ctx.foo)')
+    expect(code).contains(
+      '_foo !== _ctx.foo && _setAttr(n0, "textContent", (_foo = _ctx.foo))',
+    )
   })
 
   test('.attr modifier w/ value', () => {
     const { code } = compileWithVBind(`<div :value.attr="foo" />`)
     expect(code).matchSnapshot()
-    expect(code).contains('_setAttr(n0, "value", _ctx.foo)')
+    expect(code).contains(
+      '_foo !== _ctx.foo && _setAttr(n0, "value", (_foo = _ctx.foo))',
+    )
   })
 
   test('.attr modifier w/ progress value', () => {
     const { code } = compileWithVBind(`<progress :value.attr="foo" />`)
     expect(code).matchSnapshot()
-    expect(code).contains('_setAttr(n0, "value", _ctx.foo)')
+    expect(code).contains(
+      '_foo !== _ctx.foo && _setAttr(n0, "value", (_foo = _ctx.foo))',
+    )
   })
 
   test('attributes must be set as attribute', () => {
@@ -609,20 +655,35 @@ describe('compiler v-bind', () => {
     `)
 
     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(
+      '_spellcheck !== _ctx.spellcheck && _setAttr(n0, "spellcheck", (_spellcheck = _ctx.spellcheck))',
+    )
+    expect(code).contains(
+      '_draggable !== _ctx.draggable && _setAttr(n0, "draggable", (_draggable = _ctx.draggable))',
+    )
+    expect(code).contains(
+      '_translate !== _ctx.translate && _setAttr(n0, "translate", (_translate = _ctx.translate))',
+    )
+    expect(code).contains(
+      '_form !== _ctx.form && _setAttr(n0, "form", (_form = _ctx.form))',
+    )
+    expect(code).contains(
+      '_list !== _ctx.list && _setAttr(n1, "list", (_list = _ctx.list))',
+    )
+    expect(code).contains(
+      '_type !== _ctx.type && _setAttr(n2, "type", (_type = _ctx.type))',
+    )
+    expect(code).contains('if(_width !== _ctx.width) {')
+    expect(code).contains('if(_height !== _ctx.height) {')
+    expect(code).contains('_height = _ctx.height')
+    expect(code).contains('_height = _ctx.height')
     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)')
+    expect(code).contains(' _setAttr(n6, "width", _ctx.width)')
   })
 
   test('HTML global attributes should set as dom prop', () => {
@@ -631,11 +692,21 @@ describe('compiler v-bind', () => {
     `)
 
     expect(code).matchSnapshot()
-    expect(code).contains('_setDOMProp(n0, "id", _ctx.id)')
-    expect(code).contains('_setDOMProp(n0, "title", _ctx.title)')
-    expect(code).contains('_setDOMProp(n0, "lang", _ctx.lang)')
-    expect(code).contains('_setDOMProp(n0, "dir", _ctx.dir)')
-    expect(code).contains('_setDOMProp(n0, "tabindex", _ctx.tabindex)')
+    expect(code).contains(
+      '_id !== _ctx.id && _setDOMProp(n0, "id", (_id = _ctx.id))',
+    )
+    expect(code).contains(
+      '_title !== _ctx.title && _setDOMProp(n0, "title", (_title = _ctx.title))',
+    )
+    expect(code).contains(
+      '_lang !== _ctx.lang && _setDOMProp(n0, "lang", (_lang = _ctx.lang))',
+    )
+    expect(code).contains(
+      '_dir !== _ctx.dir && _setDOMProp(n0, "dir", (_dir = _ctx.dir))',
+    )
+    expect(code).contains(
+      '_tabindex !== _ctx.tabindex && _setDOMProp(n0, "tabindex", (_tabindex = _ctx.tabindex))',
+    )
   })
 
   test('SVG global attributes should set as dom prop', () => {
@@ -644,9 +715,15 @@ describe('compiler v-bind', () => {
     `)
 
     expect(code).matchSnapshot()
-    expect(code).contains('_setDOMProp(n0, "id", _ctx.id)')
-    expect(code).contains('_setDOMProp(n0, "lang", _ctx.lang)')
-    expect(code).contains('_setDOMProp(n0, "tabindex", _ctx.tabindex)')
+    expect(code).contains(
+      '_id !== _ctx.id && _setDOMProp(n0, "id", (_id = _ctx.id))',
+    )
+    expect(code).contains(
+      '_lang !== _ctx.lang && _setDOMProp(n0, "lang", (_lang = _ctx.lang))',
+    )
+    expect(code).contains(
+      '_tabindex !== _ctx.tabindex && _setDOMProp(n0, "tabindex", (_tabindex = _ctx.tabindex))',
+    )
   })
 
   test('MathML global attributes should set as dom prop', () => {
@@ -655,11 +732,21 @@ describe('compiler v-bind', () => {
     `)
 
     expect(code).matchSnapshot()
-    expect(code).contains('_setDOMProp(n0, "autofucus", _ctx.autofucus)')
-    expect(code).contains('_setDOMProp(n0, "dir", _ctx.dir)')
-    expect(code).contains('_setDOMProp(n0, "displaystyle", _ctx.displaystyle)')
-    expect(code).contains('_setDOMProp(n0, "mathcolor", _ctx.mathcolor)')
-    expect(code).contains('_setDOMProp(n0, "tabindex", _ctx.tabindex)')
+    expect(code).contains(
+      '_autofucus !== _ctx.autofucus && _setDOMProp(n0, "autofucus", (_autofucus = _ctx.autofucus))',
+    )
+    expect(code).contains(
+      '_dir !== _ctx.dir && _setDOMProp(n0, "dir", (_dir = _ctx.dir))',
+    )
+    expect(code).contains(
+      '_displaystyle !== _ctx.displaystyle && _setDOMProp(n0, "displaystyle", (_displaystyle = _ctx.displaystyle))',
+    )
+    expect(code).contains(
+      '_mathcolor !== _ctx.mathcolor && _setDOMProp(n0, "mathcolor", (_mathcolor = _ctx.mathcolor))',
+    )
+    expect(code).contains(
+      '_tabindex !== _ctx.tabindex && _setDOMProp(n0, "tabindex", (_tabindex = _ctx.tabindex))',
+    )
   })
 
   test(':innerHTML', () => {
@@ -667,7 +754,10 @@ describe('compiler v-bind', () => {
       <div :innerHTML="foo"/>
     `)
     expect(code).matchSnapshot()
-    expect(code).contains('_setHtml(n0, _ctx.foo)')
+    expect(code).contains('let _foo')
+    expect(code).contains(
+      '_foo !== _ctx.foo && _setHtml(n0, (_foo = _ctx.foo))',
+    )
   })
 
   test(':textContext', () => {
@@ -675,7 +765,10 @@ describe('compiler v-bind', () => {
       <div :textContent="foo"/>
     `)
     expect(code).matchSnapshot()
-    expect(code).contains('_setText(n0, _ctx.foo)')
+    expect(code).contains('let _foo')
+    expect(code).contains(
+      '_foo !== _ctx.foo && _setText(n0, (_foo = _ctx.foo))',
+    )
   })
 
   test(':value', () => {
@@ -683,7 +776,10 @@ describe('compiler v-bind', () => {
       <input :value="foo"/>
     `)
     expect(code).matchSnapshot()
-    expect(code).contains('_setValue(n0, _ctx.foo)')
+    expect(code).contains('let _foo')
+    expect(code).contains(
+      '_foo !== _ctx.foo && _setValue(n0, (_foo = _ctx.foo))',
+    )
   })
 
   test(':value w/ progress', () => {
@@ -691,7 +787,19 @@ describe('compiler v-bind', () => {
       <progress :value="foo"/>
     `)
     expect(code).matchSnapshot()
-    expect(code).contains('_setDynamicProp(n0, "value", _ctx.foo)')
+    expect(code).contains(
+      '_foo !== _ctx.foo && (_foo = _setDynamicProp(n0, "value", _foo, _ctx.foo))',
+    )
+  })
+
+  test('bind member expression', () => {
+    const { code } = compileWithVBind(`
+      <div :id="obj.count.bar"></div>/>
+    `)
+    expect(code).matchSnapshot()
+    expect(code).contains(
+      '_obj !== _ctx.obj.count.bar && _setDOMProp(n0, "id", (_obj = _ctx.obj.count.bar))',
+    )
   })
 
   test('number value', () => {
index 58cae3b884190c3fbbc8c78d30f00b925dc88d90..14bc78fd02d81b8a014c547d2958c8e4e13c6919 100644 (file)
@@ -2,7 +2,7 @@ import type {
   CodegenOptions as BaseCodegenOptions,
   BaseCodegenResult,
 } from '@vue/compiler-dom'
-import type { BlockIRNode, RootIRNode, VaporHelper } from './ir'
+import type { BlockIRNode, IREffect, RootIRNode, VaporHelper } from './ir'
 import { extend, remove } from '@vue/shared'
 import { genBlockContent } from './generators/block'
 import { genTemplates } from './generators/template'
@@ -35,6 +35,15 @@ export class CodegenContext {
 
   delegates: Set<string> = new Set<string>()
 
+  processingRenderEffect: IREffect | undefined = undefined
+  allRenderEffectSeenNames: Record<string, number> = Object.create(null)
+  shouldCacheRenderEffectDeps = (): boolean => {
+    // only need to generate effect deps when it's not nested in v-for
+    return !!(
+      this.processingRenderEffect && !this.processingRenderEffect.inVFor
+    )
+  }
+
   identifiers: Record<string, string[]> = Object.create(null)
 
   block: BlockIRNode
index d13a6641218055a1740cdf1522f236c05aa24ef2..ef0176263e9ddfab9391e1b70e5be946f9c506d3 100644 (file)
@@ -1,4 +1,4 @@
-import { isGloballyAllowed } from '@vue/shared'
+import { isArray, isGloballyAllowed } from '@vue/shared'
 import {
   BindingTypes,
   NewlineType,
@@ -95,7 +95,14 @@ export function genExpression(
         )
 
         if (i === ids.length - 1 && end < content.length) {
-          push([content.slice(end), NewlineType.Unknown])
+          const rest = content.slice(end)
+          const last = frag[frag.length - 1]
+          if (hasMemberExpression && isArray(last)) {
+            // merge rest content into the last identifier's generated name
+            last[0] += rest
+          } else {
+            push([rest, NewlineType.Unknown])
+          }
         }
       })
 
index 1b129375acd33e52ca945a88a3800504db3760e8..9175ae3729980448145ebf2919331f33a5915c12 100644 (file)
@@ -2,18 +2,17 @@ import type { CodegenContext } from '../generate'
 import type { SetHtmlIRNode } from '../ir'
 import { genExpression } from './expression'
 import { type CodeFragment, NEWLINE, genCall } from './utils'
+import { processValues } from './prop'
 
 export function genSetHtml(
   oper: SetHtmlIRNode,
   context: CodegenContext,
 ): CodeFragment[] {
-  const { vaporHelper } = context
-  return [
-    NEWLINE,
-    ...genCall(
-      vaporHelper('setHtml'),
-      `n${oper.element}`,
-      genExpression(oper.value, context),
-    ),
-  ]
+  const { vaporHelper, shouldCacheRenderEffectDeps } = context
+  const { value, element } = oper
+  let html = genExpression(value, context)
+  if (shouldCacheRenderEffectDeps()) {
+    processValues(context, [html])
+  }
+  return [NEWLINE, ...genCall(vaporHelper('setHtml'), `n${element}`, html)]
 }
index b637e797a83a36c45bb70ebd97b9a1b0beb74632..8dc196e59c31788f28d8ebe251faa0e4f659c1f8 100644 (file)
@@ -78,9 +78,33 @@ export function genEffects(
   effects: IREffect[],
   context: CodegenContext,
 ): CodeFragment[] {
-  const [frag, push] = buildCodeFragment()
-  for (const effect of effects) {
-    push(...genEffect(effect, context))
+  const { vaporHelper } = context
+  const [frag, push, unshift] = buildCodeFragment()
+  const declareNames = new Set<string>()
+  let operationsCount = 0
+  for (let i = 0; i < effects.length; i++) {
+    const effect = (context.processingRenderEffect = effects[i])
+    operationsCount += effect.operations.length
+    const frags = genEffect(effect, context, declareNames)
+    const needSemi = frag[frag.length - 1] === ')' && frags[0] === '('
+    i > 0 && push(NEWLINE)
+    push(needSemi ? ';' : undefined, ...frags)
+  }
+
+  const newLineCount = frag.filter(frag => frag === NEWLINE).length
+  if (newLineCount > 1 || operationsCount > 1) {
+    unshift(`{`, INDENT_START, NEWLINE)
+    push(INDENT_END, NEWLINE, '}')
+  }
+
+  if (effects.length) {
+    unshift(NEWLINE, `${vaporHelper('renderEffect')}(() => `)
+    push(`)`)
+  }
+
+  // declare variables: let _foo, _bar
+  if (declareNames.size) {
+    frag.splice(1, 0, `let ${[...declareNames].join(', ')}`, NEWLINE)
   }
   return frag
 }
@@ -88,21 +112,50 @@ export function genEffects(
 export function genEffect(
   { operations }: IREffect,
   context: CodegenContext,
+  allDeclareNames: Set<string>,
 ): CodeFragment[] {
-  const { vaporHelper } = context
-  const [frag, push] = buildCodeFragment(
-    NEWLINE,
-    `${vaporHelper('renderEffect')}(() => `,
-  )
+  const { processingRenderEffect } = context
+  const [frag, push] = buildCodeFragment()
+  const { declareNames, earlyCheckExps } = processingRenderEffect!
+  const operationsExps = genOperations(operations, context)
 
-  const [operationsExps, pushOps] = buildCodeFragment()
-  operations.forEach(op => pushOps(...genOperation(op, context)))
+  if (declareNames.size) {
+    allDeclareNames.add([...declareNames].join(', '))
+  }
 
   const newlineCount = operationsExps.filter(frag => frag === NEWLINE).length
   if (newlineCount > 1) {
-    push('{', INDENT_START, ...operationsExps, INDENT_END, NEWLINE, '})')
+    // multiline check expression: if (_foo !== _ctx.foo || _bar !== _ctx.bar) {
+    const checkExpsStart: CodeFragment[] =
+      earlyCheckExps.length > 0
+        ? [`if(`, ...earlyCheckExps.join(' || '), `) {`, INDENT_START]
+        : []
+    const checkExpsEnd: CodeFragment[] =
+      earlyCheckExps.length > 0 ? [INDENT_END, NEWLINE, '}'] : []
+    // assignment: _foo = _ctx.foo; _bar = _ctx.bar
+    const assignmentExps: CodeFragment[] =
+      earlyCheckExps.length > 0
+        ? [NEWLINE, ...earlyCheckExps.map(c => c.replace('!==', '=')).join(';')]
+        : []
+    push(
+      ...checkExpsStart,
+      ...operationsExps,
+      ...assignmentExps,
+      ...checkExpsEnd,
+    )
   } else {
-    push(...operationsExps.filter(frag => frag !== NEWLINE), ')')
+    // single line check expression: (_foo !== _ctx.foo || _bar !== _ctx.bar) &&
+    const multiple = earlyCheckExps.length > 1
+    const checkExps: CodeFragment[] =
+      earlyCheckExps.length > 0
+        ? [
+            multiple ? `(` : undefined,
+            ...earlyCheckExps.join(' || '),
+            multiple ? `)` : undefined,
+            ' && ',
+          ]
+        : []
+    push(...checkExps, ...operationsExps.filter(frag => frag !== NEWLINE))
   }
 
   return frag
index e30d4c7edefe1136a533a82201c78f5e331a41bf..3a0964dd1664be63dcb0bb6a4e41c1b200587cf6 100644 (file)
@@ -24,6 +24,7 @@ import {
 import {
   attributeCache,
   canSetValueDirectly,
+  isArray,
   isHTMLGlobalAttr,
   isHTMLTag,
   isMathMLGlobalAttr,
@@ -44,20 +45,30 @@ export function genSetProp(
     prop: { key, values, modifier },
     tag,
   } = oper
-
   const { helperName, omitKey } = getRuntimeHelper(tag, key.content, modifier)
+  const propValue = genPropValue(values, context)
+  const { prevValueName, shouldWrapInParentheses } = processPropValues(
+    context,
+    helperName,
+    [propValue],
+  )
   return [
     NEWLINE,
+    ...(prevValueName
+      ? [shouldWrapInParentheses ? `(` : undefined, `${prevValueName} = `]
+      : []),
     ...genCall(
       [vaporHelper(helperName), null],
       `n${oper.element}`,
       omitKey ? false : genExpression(key, context),
-      genPropValue(values, context),
+      ...(prevValueName ? [`${prevValueName}`] : []),
+      propValue,
       // only `setClass` and `setStyle` need merge inherit attr
       oper.root && (helperName === 'setClass' || helperName === 'setStyle')
         ? 'true'
         : undefined,
     ),
+    ...(prevValueName && shouldWrapInParentheses ? [`)`] : []),
   ]
 }
 
@@ -67,24 +78,31 @@ export function genDynamicProps(
   context: CodegenContext,
 ): CodeFragment[] {
   const { vaporHelper } = context
+  const values = oper.props.map(props =>
+    Array.isArray(props)
+      ? genLiteralObjectProps(props, context) // static and dynamic arg props
+      : props.kind === IRDynamicPropsKind.ATTRIBUTE
+        ? genLiteralObjectProps([props], context) // dynamic arg props
+        : genExpression(props.value, context),
+  ) // v-bind=""
+  const { prevValueName, shouldWrapInParentheses } = processPropValues(
+    context,
+    'setDynamicProps',
+    values,
+  )
   return [
     NEWLINE,
+    ...(prevValueName
+      ? [shouldWrapInParentheses ? `(` : undefined, `${prevValueName} = `]
+      : []),
     ...genCall(
       vaporHelper('setDynamicProps'),
       `n${oper.element}`,
-      genMulti(
-        DELIMITERS_ARRAY,
-        ...oper.props.map(
-          props =>
-            Array.isArray(props)
-              ? genLiteralObjectProps(props, context) // static and dynamic arg props
-              : props.kind === IRDynamicPropsKind.ATTRIBUTE
-                ? genLiteralObjectProps([props], context) // dynamic arg props
-                : genExpression(props.value, context), // v-bind=""
-        ),
-      ),
+      ...(prevValueName ? [`${prevValueName}`] : []),
+      genMulti(DELIMITERS_ARRAY, ...values),
       oper.root && 'true',
     ),
+    ...(prevValueName && shouldWrapInParentheses ? [`)`] : []),
   ]
 }
 
@@ -235,3 +253,95 @@ const getSpecialHelper = (
 
   return specialHelpers[keyName] || null
 }
+
+// those runtime helpers will return the prevValue
+const helpersNeedCachedReturnValue = [
+  'setStyle',
+  'setDynamicProp',
+  'setDynamicProps',
+]
+
+function processPropValues(
+  context: CodegenContext,
+  helperName: string,
+  values: CodeFragment[][],
+): { prevValueName: string | undefined; shouldWrapInParentheses: boolean } {
+  const { shouldCacheRenderEffectDeps, processingRenderEffect } = context
+  // single-line render effect and the operation needs cache return a value,
+  // the expression needs to be wrapped in parentheses.
+  // e.g. _foo === _ctx.foo && (_foo = _setStyle(...))
+  let shouldWrapInParentheses: boolean = false
+  let prevValueName
+  if (shouldCacheRenderEffectDeps()) {
+    const needReturnValue = helpersNeedCachedReturnValue.includes(helperName)
+    processValues(context, values, !needReturnValue)
+    const { declareNames } = processingRenderEffect!
+    // if the operation needs to cache the return value and has multiple declareNames,
+    // combine them into a single name as the return value name.
+    if (declareNames.size > 0 && needReturnValue) {
+      prevValueName = [...declareNames].join('')
+      declareNames.add(prevValueName)
+    }
+    shouldWrapInParentheses = processingRenderEffect!.operations.length === 1
+  }
+  return { prevValueName, shouldWrapInParentheses }
+}
+
+export function processValues(
+  context: CodegenContext,
+  values: CodeFragment[][],
+  needRewrite: boolean = true,
+): string[] {
+  const allCheckExps: string[] = []
+  values.forEach(value => {
+    const checkExps = processValue(context, value, needRewrite)
+    if (checkExps) allCheckExps.push(...checkExps, ' && ')
+  })
+
+  return allCheckExps.length > 0
+    ? (context.processingRenderEffect!.earlyCheckExps = [
+        ...new Set(allCheckExps),
+      ])
+    : []
+}
+
+function processValue(
+  context: CodegenContext,
+  values: CodeFragment[],
+  needRewrite: boolean = true,
+): string[] | undefined {
+  const { processingRenderEffect, allRenderEffectSeenNames } = context
+  const { declareNames, rewrittenNames, earlyCheckExps, operations } =
+    processingRenderEffect!
+
+  const isSingleLine = operations.length === 1
+  for (const frag of values) {
+    if (!isArray(frag)) continue
+    // [code, newlineIndex, loc, name] -> [(_name = code), newlineIndex, loc, name]
+    const [newName, , , rawName] = frag
+    if (rawName) {
+      let name = rawName.replace(/[^\w]/g, '_')
+      if (rewrittenNames.has(name)) continue
+      rewrittenNames.add(name)
+
+      name = `_${name}`
+      if (declareNames.has(name)) continue
+
+      if (allRenderEffectSeenNames[name] === undefined)
+        allRenderEffectSeenNames[name] = 0
+      else name += ++allRenderEffectSeenNames[name]
+
+      declareNames.add(name)
+      earlyCheckExps.push(`${name} !== ${newName}`)
+
+      if (needRewrite && isSingleLine) {
+        // replace the original code fragment with the assignment expression
+        frag[0] = `(${name} = ${newName})`
+      }
+    }
+  }
+
+  if (earlyCheckExps.length > 0) {
+    return [[...new Set(earlyCheckExps)].join(' && ')]
+  }
+}
index e433bb977bea74189695a77a8ae46237b75f20fb..7eaaaa7fce38cb59ff8c59e9ec5e8c824a2bc06f 100644 (file)
@@ -8,21 +8,19 @@ import {
   genCall,
   genMulti,
 } from './utils'
+import { processValues } from './prop'
 
 export function genSetText(
   oper: SetTextIRNode,
   context: CodegenContext,
 ): CodeFragment[] {
-  const { vaporHelper } = context
+  const { vaporHelper, shouldCacheRenderEffectDeps } = context
   const { element, values } = oper
-  return [
-    NEWLINE,
-    ...genCall(
-      vaporHelper('setText'),
-      `n${element}`,
-      ...values.map(value => genExpression(value, context)),
-    ),
-  ]
+  const texts = values.map(value => genExpression(value, context))
+  if (shouldCacheRenderEffectDeps()) {
+    processValues(context, texts)
+  }
+  return [NEWLINE, ...genCall(vaporHelper('setText'), `n${element}`, ...texts)]
 }
 
 export function genCreateTextNode(
index f001d8e928b9f50927f2c6c1ffdd49d5eab77a38..904b3dc87cac351db97a71a23db1acd32f53c239 100644 (file)
@@ -29,9 +29,14 @@ export type CodeFragments = Exclude<CodeFragment, any[]> | CodeFragment[]
 
 export function buildCodeFragment(
   ...frag: CodeFragment[]
-): [CodeFragment[], (...items: CodeFragment[]) => number] {
+): [
+  CodeFragment[],
+  (...items: CodeFragment[]) => number,
+  (...items: CodeFragment[]) => number,
+] {
   const push = frag.push.bind(frag)
-  return [frag, push]
+  const unshift = frag.unshift.bind(frag)
+  return [frag, push, unshift]
 }
 
 export type CodeFragmentDelimiters = [
index 7d1ddac894514a2ae7ad75ebff0bc4cb5f33de19..d71d5b556cc2ef7ebcea3ede62effbfccec1b16a 100644 (file)
@@ -266,7 +266,12 @@ export interface IRDynamicInfo {
 
 export interface IREffect {
   expressions: SimpleExpressionNode[]
+  identifiers: string[]
   operations: OperationNode[]
+  declareNames: Set<string>
+  rewrittenNames: Set<string>
+  earlyCheckExps: string[]
+  inVFor: boolean
 }
 
 type Overwrite<T, U> = Pick<T, Exclude<keyof T, keyof U>> &
index f4290ab89572825ab08c612ea40cc67f7214f0af..0b4f71f8d7e1c671f9c72bada0f56197b287f627 100644 (file)
@@ -5,6 +5,7 @@ import {
   type CompilerCompatOptions,
   type ElementNode,
   ElementTypes,
+  type ExpressionNode,
   NodeTypes,
   type RootNode,
   type SimpleExpressionNode,
@@ -12,8 +13,16 @@ import {
   defaultOnError,
   defaultOnWarn,
   isVSlot,
+  walkIdentifiers,
 } from '@vue/compiler-dom'
-import { EMPTY_OBJ, NOOP, extend, isArray, isString } from '@vue/shared'
+import {
+  EMPTY_OBJ,
+  NOOP,
+  extend,
+  isArray,
+  isString,
+  looseEqual,
+} from '@vue/shared'
 import {
   type BlockIRNode,
   DynamicFlag,
@@ -142,8 +151,10 @@ export class TransformContext<T extends AllNode = AllNode> {
     if (this.inVOnce || expressions.length === 0) {
       return this.registerOperation(...operations)
     }
+    const ids = new Set<string>()
+    expressions.forEach(exp => extractIdentifiers(ids, exp))
     const existing = this.block.effect.find(e =>
-      isSameExpression(e.expressions, expressions),
+      looseEqual(e.identifiers, Array.from(ids)),
     )
     if (existing) {
       existing.operations.push(...operations)
@@ -151,16 +162,13 @@ export class TransformContext<T extends AllNode = AllNode> {
       this.block.effect.push({
         expressions,
         operations,
+        earlyCheckExps: [],
+        declareNames: new Set<string>(),
+        rewrittenNames: new Set<string>(),
+        inVFor: this.inVFor > 0,
+        identifiers: Array.from(ids),
       })
     }
-
-    function isSameExpression(
-      a: SimpleExpressionNode[],
-      b: SimpleExpressionNode[],
-    ) {
-      if (a.length !== b.length) return false
-      return a.every((exp, i) => exp.content === b[i].content)
-    }
   }
   registerOperation(...node: OperationNode[]): void {
     this.block.operation.push(...node)
@@ -296,3 +304,11 @@ export function createStructuralDirectiveTransform(
     }
   }
 }
+
+function extractIdentifiers(ids: Set<string>, node: ExpressionNode) {
+  if (node.ast) {
+    walkIdentifiers(node.ast, n => ids.add(n.name), true)
+  } else if (node.ast === null) {
+    ids.add((node as SimpleExpressionNode).content)
+  }
+}
index 3444c34076cb0634783205b4a306b767551a4ebd..e94cd2196caab32daf2ffeba96dab69c45dc5dd2 100644 (file)
@@ -78,7 +78,8 @@ describe('api: setup context', () => {
       inheritAttrs: false,
       setup(props, { attrs }) {
         const el = document.createElement('div')
-        renderEffect(() => setDynamicProps(el, [attrs]))
+        let prev: any
+        renderEffect(() => (prev = setDynamicProps(el, prev, [attrs])))
         return el
       },
     })
@@ -115,7 +116,10 @@ describe('api: setup context', () => {
         const n0 = createComponent(Wrapper, null, {
           default: () => {
             const n0 = template('<div>')() as HTMLDivElement
-            renderEffect(() => setDynamicProps(n0, [attrs], true))
+            let prev: any
+            renderEffect(
+              () => (prev = setDynamicProps(n0, prev, [attrs], true)),
+            )
             return n0
           },
         })
index 50a39cea9bf54bda1545750f5832644883122b9b..6f6666e4c4a1d0adc3d09d2013a128aa45ce0cd6 100644 (file)
@@ -14,7 +14,6 @@ import {
   ComponentInternalInstance,
   setCurrentInstance,
 } from '../../src/component'
-import { getMetadata, recordPropMetadata } from '../../src/componentMetadata'
 import { getCurrentScope } from '@vue/reactivity'
 
 let removeComponentInstance = NOOP
@@ -35,34 +34,6 @@ afterEach(() => {
 })
 
 describe('patchProp', () => {
-  describe('recordPropMetadata', () => {
-    test('should record prop metadata', () => {
-      const node = {} as Node // the node is just a key
-      let prev = recordPropMetadata(node, 'class', 'foo')
-      expect(prev).toBeUndefined()
-      prev = recordPropMetadata(node, 'class', 'bar')
-      expect(prev).toBe('foo')
-      prev = recordPropMetadata(node, 'style', 'color: red')
-      expect(prev).toBeUndefined()
-      prev = recordPropMetadata(node, 'style', 'color: blue')
-      expect(prev).toBe('color: red')
-
-      expect(getMetadata(node)).toEqual([
-        { class: 'bar', style: 'color: blue' },
-        {},
-      ])
-    })
-
-    test('should have different metadata for different nodes', () => {
-      const node1 = {} as Node
-      const node2 = {} as Node
-      recordPropMetadata(node1, 'class', 'foo')
-      recordPropMetadata(node2, 'class', 'bar')
-      expect(getMetadata(node1)).toEqual([{ class: 'foo' }, {}])
-      expect(getMetadata(node2)).toEqual([{ class: 'bar' }, {}])
-    })
-  })
-
   describe('setClass', () => {
     test('should set class', () => {
       const el = document.createElement('div')
@@ -78,83 +49,87 @@ describe('patchProp', () => {
   describe('setStyle', () => {
     test('should set style', () => {
       const el = document.createElement('div')
-      setStyle(el, 'color: red')
+      setStyle(el, '', 'color: red')
       expect(el.style.cssText).toBe('color: red;')
     })
 
     test('should work with camelCase', () => {
       const el = document.createElement('div')
-      setStyle(el, { fontSize: '12px' })
+      setStyle(el, null, { fontSize: '12px' })
       expect(el.style.cssText).toBe('font-size: 12px;')
     })
 
     test('shoud set style with object and array property', () => {
       const el = document.createElement('div')
-      setStyle(el, { color: 'red' })
+      let prev: any
+      prev = setStyle(el, prev, { color: 'red' })
       expect(el.style.cssText).toBe('color: red;')
-      setStyle(el, [{ color: 'blue' }, { fontSize: '12px' }])
+      setStyle(el, prev, [{ color: 'blue' }, { fontSize: '12px' }])
       expect(el.style.cssText).toBe('color: blue; font-size: 12px;')
     })
 
     test('should remove if falsy value', () => {
       const el = document.createElement('div')
-      setStyle(el, { color: undefined, borderRadius: null })
+      let prev
+      prev = setStyle(el, prev, { color: undefined, borderRadius: null })
       expect(el.style.cssText).toBe('')
-      setStyle(el, { color: 'red' })
+      prev = setStyle(el, prev, { color: 'red' })
       expect(el.style.cssText).toBe('color: red;')
-      setStyle(el, { color: undefined, borderRadius: null })
+      setStyle(el, prev, { color: undefined, borderRadius: null })
       expect(el.style.cssText).toBe('')
     })
 
     test('should work with !important', () => {
       const el = document.createElement('div')
-      setStyle(el, { color: 'red !important' })
+      setStyle(el, null, { color: 'red !important' })
       expect(el.style.cssText).toBe('color: red !important;')
     })
 
     test('should work with camelCase and !important', () => {
       const el = document.createElement('div')
-      setStyle(el, { fontSize: '12px !important' })
+      setStyle(el, null, { fontSize: '12px !important' })
       expect(el.style.cssText).toBe('font-size: 12px !important;')
     })
 
     test('should work with multiple entries', () => {
       const el = document.createElement('div')
-      setStyle(el, { color: 'red', marginRight: '10px' })
+      setStyle(el, null, { color: 'red', marginRight: '10px' })
       expect(el.style.getPropertyValue('color')).toBe('red')
       expect(el.style.getPropertyValue('margin-right')).toBe('10px')
     })
 
     test('should patch with falsy style value', () => {
       const el = document.createElement('div')
-      setStyle(el, { width: '100px' })
+      let prev: any
+      prev = setStyle(el, prev, { width: '100px' })
       expect(el.style.cssText).toBe('width: 100px;')
-      setStyle(el, { width: 0 })
+      prev = setStyle(el, prev, { width: 0 })
       expect(el.style.cssText).toBe('width: 0px;')
     })
 
     test('should remove style attribute on falsy value', () => {
       const el = document.createElement('div')
-      setStyle(el, { width: '100px' })
+      let prev: any
+      prev = setStyle(el, prev, { width: '100px' })
       expect(el.style.cssText).toBe('width: 100px;')
-      setStyle(el, { width: undefined })
+      prev = setStyle(el, prev, { width: undefined })
       expect(el.style.cssText).toBe('')
 
-      setStyle(el, { width: '100px' })
+      prev = setStyle(el, prev, { width: '100px' })
       expect(el.style.cssText).toBe('width: 100px;')
-      setStyle(el, null)
+      setStyle(el, prev, null)
       expect(el.hasAttribute('style')).toBe(false)
       expect(el.style.cssText).toBe('')
     })
 
     test('should warn for trailing semicolons', () => {
       const el = document.createElement('div')
-      setStyle(el, { color: 'red;' })
+      setStyle(el, null, { color: 'red;' })
       expect(
         `Unexpected semicolon at the end of 'color' style value: 'red;'`,
       ).toHaveBeenWarned()
 
-      setStyle(el, { '--custom': '100; ' })
+      setStyle(el, null, { '--custom': '100; ' })
       expect(
         `Unexpected semicolon at the end of '--custom' style value: '100; '`,
       ).toHaveBeenWarned()
@@ -162,13 +137,16 @@ describe('patchProp', () => {
 
     test('should not warn for trailing semicolons', () => {
       const el = document.createElement('div')
-      setStyle(el, { '--custom': '100\\;' })
+      setStyle(el, null, { '--custom': '100\\;' })
       expect(el.style.getPropertyValue('--custom')).toBe('100\\;')
     })
 
     test('should work with shorthand properties', () => {
       const el = document.createElement('div')
-      setStyle(el, { borderBottom: '1px solid red', border: '1px solid green' })
+      setStyle(el, null, {
+        borderBottom: '1px solid red',
+        border: '1px solid green',
+      })
       expect(el.style.border).toBe('1px solid green')
       expect(el.style.borderBottom).toBe('1px solid green')
     })
@@ -193,19 +171,21 @@ describe('patchProp', () => {
 
     test('should work with css custom properties', () => {
       const el = mockElementWithStyle()
-      setStyle(el as any, { '--theme': 'red' })
+      setStyle(el as any, null, { '--theme': 'red' })
       expect(el.style.getPropertyValue('--theme')).toBe('red')
     })
 
     test('should auto vendor prefixing', () => {
       const el = mockElementWithStyle()
-      setStyle(el as any, { transition: 'all 1s' })
+      setStyle(el as any, null, { transition: 'all 1s' })
       expect(el.style.WebkitTransition).toBe('all 1s')
     })
 
     test('should work with multiple values', () => {
       const el = mockElementWithStyle()
-      setStyle(el as any, { display: ['-webkit-box', '-ms-flexbox', 'flex'] })
+      setStyle(el as any, null, {
+        display: ['-webkit-box', '-ms-flexbox', 'flex'],
+      })
       expect(el.style.display).toBe('flex')
     })
   })
@@ -335,12 +315,13 @@ describe('patchProp', () => {
 
   describe('setDynamicProp', () => {
     const element = document.createElement('div')
+    let prev: any
     function setDynamicProp(
       key: string,
       value: any,
       el = element.cloneNode(true) as HTMLElement,
     ) {
-      _setDynamicProp(el, key, value)
+      prev = _setDynamicProp(el, key, prev, value)
       return el
     }
 
@@ -397,25 +378,25 @@ describe('patchProp', () => {
   describe('setDynamicProps', () => {
     test('basic set dynamic props', () => {
       const el = document.createElement('div')
-      setDynamicProps(el, [{ foo: 'val' }, { bar: 'val' }])
+      setDynamicProps(el, null, [{ foo: 'val' }, { bar: 'val' }])
       expect(el.getAttribute('foo')).toBe('val')
       expect(el.getAttribute('bar')).toBe('val')
     })
 
     test('should merge props', () => {
       const el = document.createElement('div')
-      setDynamicProps(el, [{ foo: 'val' }, { foo: 'newVal' }])
+      setDynamicProps(el, null, [{ foo: 'val' }, { foo: 'newVal' }])
       expect(el.getAttribute('foo')).toBe('newVal')
     })
 
     test('should reset old props', () => {
       const el = document.createElement('div')
-
-      setDynamicProps(el, [{ foo: 'val' }])
+      let prev: any
+      prev = setDynamicProps(el, prev, [{ foo: 'val' }])
       expect(el.attributes.length).toBe(1)
       expect(el.getAttribute('foo')).toBe('val')
 
-      setDynamicProps(el, [{ bar: 'val' }])
+      prev = setDynamicProps(el, prev, [{ bar: 'val' }])
       expect(el.attributes.length).toBe(1)
       expect(el.getAttribute('bar')).toBe('val')
       expect(el.getAttribute('foo')).toBeNull()
@@ -424,18 +405,19 @@ describe('patchProp', () => {
     test('should reset old modifier props', () => {
       const el = document.createElement('div')
 
-      setDynamicProps(el, [{ ['.foo']: 'val' }])
+      let prev: any
+      prev = setDynamicProps(el, prev, [{ ['.foo']: 'val' }])
       expect((el as any).foo).toBe('val')
 
-      setDynamicProps(el, [{ ['.bar']: 'val' }])
+      prev = setDynamicProps(el, prev, [{ ['.bar']: 'val' }])
       expect((el as any).bar).toBe('val')
       expect((el as any).foo).toBe('')
 
-      setDynamicProps(el, [{ ['^foo']: 'val' }])
+      prev = setDynamicProps(el, prev, [{ ['^foo']: 'val' }])
       expect(el.attributes.length).toBe(1)
       expect(el.getAttribute('foo')).toBe('val')
 
-      setDynamicProps(el, [{ ['^bar']: 'val' }])
+      prev = setDynamicProps(el, prev, [{ ['^bar']: 'val' }])
       expect(el.attributes.length).toBe(1)
       expect(el.getAttribute('bar')).toBe('val')
       expect(el.getAttribute('foo')).toBeNull()
index 635b45a7a490fab39932de8f15d9b78e26c295fb..6835977832e8a14f5b0b89da55b8d81973ee6ad5 100644 (file)
@@ -119,6 +119,7 @@ export function fallThroughAttrs(
     }
   }
 
+  let prevAttrs = instance.attrs
   renderEffect(() => {
     for (const key in instance.attrs) {
       if (dynamicAttrs && dynamicAttrs.includes(key)) continue
@@ -130,7 +131,7 @@ export function fallThroughAttrs(
         value = instance.attrs[key]
       }
 
-      setDynamicProp(element, key, value)
+      setDynamicProp(element, key, prevAttrs[key], value)
     }
   })
 }
index adceb5446e77677a4c36d9f1c0c2c9a6db097d53..ce0e82e5da6677e82ab15f0de8e8905e9fec8076 100644 (file)
@@ -24,6 +24,7 @@ export function fallbackComponent(
   if (rawProps || Object.keys(instance.attrs).length) {
     rawProps = [() => instance.attrs, ...normalizeRawProps(rawProps)]
 
+    let prevValue: any, prevStyle: any
     renderEffect(() => {
       let classes: unknown[] | undefined
       let styles: unknown[] | undefined
@@ -34,12 +35,16 @@ export function fallbackComponent(
           const value = getter ? valueOrGetter() : valueOrGetter
           if (key === 'class') (classes ||= []).push(value)
           else if (key === 'style') (styles ||= []).push(value)
-          else setDynamicProp(el, key, value)
+          else {
+            prevValue = setDynamicProp(el, key, prevValue, value)
+          }
         },
       )
 
       if (classes) setClass(el, classes)
-      if (styles) setStyle(el, styles)
+      if (styles) {
+        prevStyle = setStyle(el, prevStyle, styles)
+      }
     })
   }
 
index ab2ad0bc65031c2c9c2d8df82e4cd22a7771ca1d..4160d083c083b55a49cf1aca5e0543da8914b85d 100644 (file)
@@ -18,13 +18,6 @@ export function getMetadata(
   return el.$$metadata || (el.$$metadata = [{}, {}])
 }
 
-export function recordPropMetadata(el: Node, key: string, value: any): any {
-  const metadata = getMetadata(el)[MetadataKind.prop]
-  const prev = metadata[key]
-  if (prev !== value) metadata[key] = value
-  return prev
-}
-
 export function recordEventMetadata(el: Node, key: string, value: any) {
   const metadata = getMetadata(el)[MetadataKind.event]
   const handlers = (metadata[key] ||= [])
index bd6ce1f12d8917a9e3eae9578f16a75c2631fdbd..e43cff1d93b7155ff4f8151a759b9e39abfc38f8 100644 (file)
@@ -14,11 +14,6 @@ import {
 } from '@vue/shared'
 import { warn } from '../warning'
 import { setStyle } from './style'
-import {
-  MetadataKind,
-  getMetadata,
-  recordPropMetadata,
-} from '../componentMetadata'
 import { on } from './event'
 import type { Data } from '@vue/runtime-shared'
 import { currentInstance } from '../component'
@@ -29,32 +24,18 @@ export function mergeInheritAttr(key: string, value: any): unknown {
 }
 
 export function setClass(el: Element, value: any, root?: boolean): void {
-  const prev = recordPropMetadata(
-    el,
-    'class',
-    (value = normalizeClass(root ? mergeInheritAttr('class', value) : value)),
-  )
-
-  if (value !== prev && (value || prev)) {
-    el.className = value
-  }
+  el.className = normalizeClass(root ? mergeInheritAttr('class', value) : value)
 }
 
 export function setAttr(el: Element, key: string, value: any): void {
-  const oldVal = recordPropMetadata(el, key, value)
-  if (value !== oldVal) {
-    if (value != null) {
-      el.setAttribute(key, value)
-    } else {
-      el.removeAttribute(key)
-    }
+  if (value != null) {
+    el.setAttribute(key, value)
+  } else {
+    el.removeAttribute(key)
   }
 }
 
 export function setValue(el: any, value: any): void {
-  const oldVal = recordPropMetadata(el, 'value', value)
-  if (value === oldVal) return
-
   // store value as _value as well since
   // non-string values will be stringified.
   el._value = value
@@ -71,9 +52,6 @@ export function setValue(el: any, value: any): void {
 }
 
 export function setDOMProp(el: any, key: string, value: any): void {
-  const oldVal = recordPropMetadata(el, key, value)
-  if (value === oldVal) return
-
   let needRemove = false
   if (value === '' || value == null) {
     const type = typeof el[key]
@@ -109,13 +87,18 @@ export function setDOMProp(el: any, key: string, value: any): void {
   needRemove && el.removeAttribute(key)
 }
 
-export function setDynamicProp(el: Element, key: string, value: any): void {
+export function setDynamicProp(
+  el: Element,
+  key: string,
+  prev: any,
+  value: any,
+): any {
   // TODO
   const isSVG = false
   if (key === 'class') {
     setClass(el, value)
   } else if (key === 'style') {
-    setStyle(el as HTMLElement, value)
+    return setStyle(el as HTMLElement, prev, value)
   } else if (isOn(key)) {
     on(el, key[2].toLowerCase() + key.slice(3), () => value, { effect: true })
   } else if (
@@ -150,30 +133,42 @@ export function setDynamicProp(el: Element, key: string, value: any): void {
 
 export function setDynamicProps(
   el: Element,
+  oldProps: any,
   args: any[],
   root?: boolean,
 ): void {
-  const oldProps = getMetadata(el)[MetadataKind.prop]
+  // const oldProps = getMetadata(el)[MetadataKind.prop]
   if (root) {
     args.unshift(currentInstance!.attrs)
   }
   const props = args.length > 1 ? mergeProps(...args) : args[0]
 
-  for (const key in oldProps) {
-    // TODO should these keys be allowed as dynamic keys? The current logic of the runtime-core will throw an error
-    if (key === 'textContent' || key === 'innerHTML') {
-      continue
-    }
+  if (oldProps) {
+    for (const key in oldProps) {
+      // TODO should these keys be allowed as dynamic keys? The current logic of the runtime-core will throw an error
+      if (key === 'textContent' || key === 'innerHTML') {
+        continue
+      }
 
-    const hasNewValue = props[key] || props['.' + key] || props['^' + key]
-    if (oldProps[key] && !hasNewValue) {
-      setDynamicProp(el, key, null)
+      const oldValue = oldProps[key]
+      const hasNewValue = props[key] || props['.' + key] || props['^' + key]
+      if (oldValue && !hasNewValue) {
+        setDynamicProp(el, key, oldValue, null)
+      }
     }
   }
 
+  const prev = Object.create(null)
   for (const key in props) {
-    setDynamicProp(el, key, props[key])
+    setDynamicProp(
+      el,
+      key,
+      oldProps ? oldProps[key] : undefined,
+      (prev[key] = props[key]),
+    )
   }
+
+  return prev
 }
 
 export function mergeProp(
@@ -213,18 +208,11 @@ export function mergeProps(...args: Data[]): Data {
 }
 
 export function setText(el: Node, ...values: any[]): void {
-  const text = values.map(v => toDisplayString(v)).join('')
-  const oldVal = recordPropMetadata(el, 'textContent', text)
-  if (text !== oldVal) {
-    el.textContent = text
-  }
+  el.textContent = values.map(v => toDisplayString(v)).join('')
 }
 
 export function setHtml(el: Element, value: any): void {
-  const oldVal = recordPropMetadata(el, 'innerHTML', value)
-  if (value !== oldVal) {
-    el.innerHTML = value == null ? '' : value
-  }
+  el.innerHTML = value == null ? '' : value
 }
 
 // TODO copied from runtime-dom
index 5ee233a0cdbd465f26c665ff3bcad6586fa24707..213dfec4b7d66595952933c4285fe67e39aced20 100644 (file)
@@ -7,16 +7,17 @@ import {
   normalizeStyle,
 } from '@vue/shared'
 import { warn } from '../warning'
-import { recordPropMetadata } from '../componentMetadata'
 import { mergeInheritAttr } from './prop'
 
-export function setStyle(el: HTMLElement, value: any, root?: boolean): void {
-  const prev = recordPropMetadata(
-    el,
-    'style',
-    (value = normalizeStyle(root ? mergeInheritAttr('style', value) : value)),
-  )
+export function setStyle(
+  el: HTMLElement,
+  prev: any,
+  value: any,
+  root?: boolean,
+): any {
+  value = normalizeStyle(root ? mergeInheritAttr('style', value) : value)
   patchStyle(el, prev, value)
+  return value
 }
 
 // TODO copied from packages/runtime-dom/src/modules/style.ts