]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
perf(runtime-vapor): optimize `setDOMProp` on static tag + key (#294)
authoredison <daiwei521@126.com>
Sun, 1 Dec 2024 09:04:42 +0000 (17:04 +0800)
committerGitHub <noreply@github.com>
Sun, 1 Dec 2024 09:04:42 +0000 (17:04 +0800)
packages/compiler-vapor/__tests__/__snapshots__/compile.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__/vOnce.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
packages/compiler-vapor/src/generators/prop.ts
packages/runtime-vapor/__tests__/dom/prop.spec.ts
packages/runtime-vapor/src/dom/prop.ts
packages/runtime-vapor/src/index.ts
packages/shared/src/general.ts

index bc827dd732fb2ba890f53aa2e0a8d8881285ddba..09e8c4ccaf614df826cef26dd280e630f27247bc 100644 (file)
@@ -186,7 +186,7 @@ export function render(_ctx) {
   _delegate(n0, "click", () => _ctx.handleClick)
   _setInheritAttrs(["id"])
   _renderEffect(() => _setText(n0, _ctx.count, "foo", _ctx.count, "foo", _ctx.count))
-  _renderEffect(() => _setDOMProp(n0, "id", _ctx.count, true))
+  _renderEffect(() => _setDOMProp(n0, "id", _ctx.count))
   return n0
 }"
 `;
index 822a6af66c24874bab838172de6a7543db6c0eac..4630eadfb55415b28f7f3aea9cdcf1fef49f59fd 100644 (file)
@@ -7,7 +7,19 @@ const t0 = _template("<div></div>")
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["foo-bar"])
-  _renderEffect(() => _setAttr(n0, "foo-bar", _ctx.id, true))
+  _renderEffect(() => _setAttr(n0, "foo-bar", _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';
+const t0 = _template("<div></div>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  _setInheritAttrs(["innerHTML"])
+  _renderEffect(() => _setAttr(n0, "innerHTML", _ctx.foo))
   return n0
 }"
 `;
@@ -19,7 +31,43 @@ const t0 = _template("<div></div>")
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["foo-bar"])
-  _renderEffect(() => _setAttr(n0, "foo-bar", _ctx.fooBar, true))
+  _renderEffect(() => _setAttr(n0, "foo-bar", _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';
+const t0 = _template("<progress></progress>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  _setInheritAttrs(["value"])
+  _renderEffect(() => _setAttr(n0, "value", _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';
+const t0 = _template("<div></div>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  _setInheritAttrs(["textContent"])
+  _renderEffect(() => _setAttr(n0, "textContent", _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';
+const t0 = _template("<div></div>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  _setInheritAttrs(["value"])
+  _renderEffect(() => _setAttr(n0, "value", _ctx.foo))
   return n0
 }"
 `;
@@ -31,7 +79,7 @@ const t0 = _template("<div></div>")
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["fooBar"])
-  _renderEffect(() => _setDynamicProp(n0, "fooBar", _ctx.id, true))
+  _renderEffect(() => _setDynamicProp(n0, "fooBar", _ctx.id))
   return n0
 }"
 `;
@@ -56,31 +104,79 @@ const t0 = _template("<div></div>")
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["fooBar"])
-  _renderEffect(() => _setDynamicProp(n0, "fooBar", _ctx.fooBar, true))
+  _renderEffect(() => _setDynamicProp(n0, "fooBar", _ctx.fooBar))
   return n0
 }"
 `;
 
-exports[`compiler v-bind > .prop modifier (shortband) w/ no expression 1`] = `
+exports[`compiler v-bind > .prop modifier (shorthand) 1`] = `
 "import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["fooBar"])
-  _renderEffect(() => _setDOMProp(n0, "fooBar", _ctx.fooBar, true))
+  _renderEffect(() => _setDOMProp(n0, "fooBar", _ctx.id))
   return n0
 }"
 `;
 
-exports[`compiler v-bind > .prop modifier (shorthand) 1`] = `
+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';
+const t0 = _template("<div></div>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  _setInheritAttrs(["innerHTML"])
+  _renderEffect(() => _setHtml(n0, _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';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["fooBar"])
-  _renderEffect(() => _setDOMProp(n0, "fooBar", _ctx.id, true))
+  _renderEffect(() => _setDOMProp(n0, "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';
+const t0 = _template("<progress></progress>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  _setInheritAttrs(["value"])
+  _renderEffect(() => _setDOMProp(n0, "value", _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';
+const t0 = _template("<div></div>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  _setInheritAttrs(["textContent"])
+  _renderEffect(() => _setText(n0, _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';
+const t0 = _template("<div></div>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  _setInheritAttrs(["value"])
+  _renderEffect(() => _setValue(n0, _ctx.foo))
   return n0
 }"
 `;
@@ -92,7 +188,7 @@ const t0 = _template("<div></div>")
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["fooBar"])
-  _renderEffect(() => _setDOMProp(n0, "fooBar", _ctx.id, true))
+  _renderEffect(() => _setDOMProp(n0, "fooBar", _ctx.id))
   return n0
 }"
 `;
@@ -109,6 +205,18 @@ export function render(_ctx) {
 }"
 `;
 
+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';
+const t0 = _template("<div></div>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  _setInheritAttrs(["innerHTML"])
+  _renderEffect(() => _setHtml(n0, _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';
 const t0 = _template("<div></div>")
@@ -116,7 +224,91 @@ const t0 = _template("<div></div>")
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["fooBar"])
-  _renderEffect(() => _setDOMProp(n0, "fooBar", _ctx.fooBar, true))
+  _renderEffect(() => _setDOMProp(n0, "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';
+const t0 = _template("<progress></progress>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  _setInheritAttrs(["value"])
+  _renderEffect(() => _setDOMProp(n0, "value", _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';
+const t0 = _template("<div></div>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  _setInheritAttrs(["textContent"])
+  _renderEffect(() => _setText(n0, _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';
+const t0 = _template("<div></div>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  _setInheritAttrs(["value"])
+  _renderEffect(() => _setValue(n0, _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';
+const t0 = _template("<div></div>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  _setInheritAttrs(["innerHTML"])
+  _renderEffect(() => _setHtml(n0, _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';
+const t0 = _template("<div></div>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  _setInheritAttrs(["textContent"])
+  _renderEffect(() => _setText(n0, _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';
+const t0 = _template("<input>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  _setInheritAttrs(["value"])
+  _renderEffect(() => _setValue(n0, _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';
+const t0 = _template("<progress></progress>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  _setInheritAttrs(["value"])
+  _renderEffect(() => _setDynamicProp(n0, "value", _ctx.foo))
   return n0
 }"
 `;
@@ -128,11 +320,11 @@ const t0 = _template("<div></div>")
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["id", "title", "lang", "dir", "tabindex"])
-  _renderEffect(() => _setDOMProp(n0, "id", _ctx.id, true))
-  _renderEffect(() => _setDOMProp(n0, "title", _ctx.title, true))
-  _renderEffect(() => _setDOMProp(n0, "lang", _ctx.lang, true))
-  _renderEffect(() => _setDOMProp(n0, "dir", _ctx.dir, true))
-  _renderEffect(() => _setDOMProp(n0, "tabindex", _ctx.tabindex, true))
+  _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))
   return n0
 }"
 `;
@@ -144,11 +336,11 @@ const t0 = _template("<math></math>")
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["autofucus", "dir", "displaystyle", "mathcolor", "tabindex"])
-  _renderEffect(() => _setDOMProp(n0, "autofucus", _ctx.autofucus, true))
-  _renderEffect(() => _setDOMProp(n0, "dir", _ctx.dir, true))
-  _renderEffect(() => _setDOMProp(n0, "displaystyle", _ctx.displaystyle, true))
-  _renderEffect(() => _setDOMProp(n0, "mathcolor", _ctx.mathcolor, true))
-  _renderEffect(() => _setDOMProp(n0, "tabindex", _ctx.tabindex, true))
+  _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))
   return n0
 }"
 `;
@@ -160,9 +352,9 @@ const t0 = _template("<svg></svg>")
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["id", "lang", "tabindex"])
-  _renderEffect(() => _setDOMProp(n0, "id", _ctx.id, true))
-  _renderEffect(() => _setDOMProp(n0, "lang", _ctx.lang, true))
-  _renderEffect(() => _setDOMProp(n0, "tabindex", _ctx.tabindex, true))
+  _renderEffect(() => _setDOMProp(n0, "id", _ctx.id))
+  _renderEffect(() => _setDOMProp(n0, "lang", _ctx.lang))
+  _renderEffect(() => _setDOMProp(n0, "tabindex", _ctx.tabindex))
   return n0
 }"
 `;
@@ -214,7 +406,7 @@ const t0 = _template("<div></div>")
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["id"])
-  _renderEffect(() => _setDOMProp(n0, "id", _ctx.id, true))
+  _renderEffect(() => _setDOMProp(n0, "id", _ctx.id))
   return n0
 }"
 `;
@@ -250,7 +442,7 @@ const t0 = _template("<div></div>")
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["camel-case"])
-  _renderEffect(() => _setDynamicProp(n0, "camel-case", _ctx.camelCase, true))
+  _renderEffect(() => _setDynamicProp(n0, "camel-case", _ctx.camelCase))
   return n0
 }"
 `;
@@ -262,7 +454,7 @@ const t0 = _template("<div></div>")
 export function render(_ctx) {
   const n0 = t0()
   _setInheritAttrs(["id"])
-  _renderEffect(() => _setDOMProp(n0, "id", _ctx.id, true))
+  _renderEffect(() => _setDOMProp(n0, "id", _ctx.id))
   return n0
 }"
 `;
index 344f7ac5a2eacd06101ce43f9a63711b208d39e3..a82221728a3aeddfb6492d86a8d7ba8ae72ff351 100644 (file)
@@ -52,8 +52,8 @@ export function render(_ctx) {
   const n0 = _createFor(() => (_ctx.items), (_ctx0) => {
     const n2 = t0()
     _setInheritAttrs(["item", "index"])
-    _renderEffect(() => _setDynamicProp(n2, "item", _ctx0[0].value, true))
-    _renderEffect(() => _setDynamicProp(n2, "index", _ctx0[1].value, true))
+    _renderEffect(() => _setDynamicProp(n2, "item", _ctx0[0].value))
+    _renderEffect(() => _setDynamicProp(n2, "index", _ctx0[1].value))
     return n2
   })
   return n0
index 2665b615750603b56a3c5a3185d13c4eabd31f5e..eacf622a77f46638af5553d85cc90e76a32c302c 100644 (file)
@@ -6,7 +6,7 @@ const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const n0 = t0()
-  _setDOMProp(n0, "id", _ctx.foo, true)
+  _setDOMProp(n0, "id", _ctx.foo)
   _setInheritAttrs(["id"])
   return n0
 }"
index 7f14bb25d02590c92b4e089a67674e1be24fff7f..efaa9257ceb068d22e3aa6f780692e7ab5a1d9be 100644 (file)
@@ -74,7 +74,7 @@ describe('compiler v-bind', () => {
     })
 
     expect(code).matchSnapshot()
-    expect(code).contains('_setDOMProp(n0, "id", _ctx.id, true)')
+    expect(code).contains('_setDOMProp(n0, "id", _ctx.id)')
   })
 
   test('no expression', () => {
@@ -104,7 +104,7 @@ describe('compiler v-bind', () => {
         ],
       },
     })
-    expect(code).contains('_setDOMProp(n0, "id", _ctx.id, true)')
+    expect(code).contains('_setDOMProp(n0, "id", _ctx.id)')
   })
 
   test('no expression (shorthand)', () => {
@@ -126,9 +126,7 @@ describe('compiler v-bind', () => {
         ],
       },
     })
-    expect(code).contains(
-      '_setDynamicProp(n0, "camel-case", _ctx.camelCase, true)',
-    )
+    expect(code).contains('_setDynamicProp(n0, "camel-case", _ctx.camelCase)')
   })
 
   test('dynamic arg', () => {
@@ -288,7 +286,7 @@ describe('compiler v-bind', () => {
     })
 
     expect(code).matchSnapshot()
-    expect(code).contains('_setDynamicProp(n0, "fooBar", _ctx.id, true)')
+    expect(code).contains('_setDynamicProp(n0, "fooBar", _ctx.id)')
   })
 
   test('.camel modifier w/ no expression', () => {
@@ -312,7 +310,7 @@ describe('compiler v-bind', () => {
       },
     })
     expect(code).contains('renderEffect')
-    expect(code).contains('_setDynamicProp(n0, "fooBar", _ctx.fooBar, true)')
+    expect(code).contains('_setDynamicProp(n0, "fooBar", _ctx.fooBar)')
   })
 
   test('.camel modifier w/ dynamic arg', () => {
@@ -370,7 +368,7 @@ describe('compiler v-bind', () => {
       },
     })
     expect(code).contains('renderEffect')
-    expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.id, true)')
+    expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.id)')
   })
 
   test('.prop modifier w/ no expression', () => {
@@ -394,7 +392,7 @@ describe('compiler v-bind', () => {
       },
     })
     expect(code).contains('renderEffect')
-    expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.fooBar, true)')
+    expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.fooBar)')
   })
 
   test('.prop modifier w/ dynamic arg', () => {
@@ -451,10 +449,10 @@ describe('compiler v-bind', () => {
       },
     })
     expect(code).contains('renderEffect')
-    expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.id, true)')
+    expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.id)')
   })
 
-  test('.prop modifier (shortband) w/ no expression', () => {
+  test('.prop modifier (shorthand) w/ no expression', () => {
     const { ir, code } = compileWithVBind(`<div .fooBar />`)
 
     expect(code).matchSnapshot()
@@ -475,7 +473,55 @@ describe('compiler v-bind', () => {
       },
     })
     expect(code).contains('renderEffect')
-    expect(code).contains('_setDOMProp(n0, "fooBar", _ctx.fooBar, true)')
+    expect(code).contains('_setDOMProp(n0, "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)')
+  })
+
+  test('.prop modifier (shorthand) w/ innerHTML', () => {
+    const { code } = compileWithVBind(`<div .innerHTML="foo" />`)
+    expect(code).matchSnapshot()
+    expect(code).contains('_setHtml(n0, _ctx.foo)')
+  })
+
+  test('.prop modifier w/ textContent', () => {
+    const { code } = compileWithVBind(`<div :textContent.prop="foo" />`)
+    expect(code).matchSnapshot()
+    expect(code).contains('_setText(n0, _ctx.foo)')
+  })
+
+  test('.prop modifier (shorthand) w/ textContent', () => {
+    const { code } = compileWithVBind(`<div .textContent="foo" />`)
+    expect(code).matchSnapshot()
+    expect(code).contains('_setText(n0, _ctx.foo)')
+  })
+
+  test('.prop modifier w/ value', () => {
+    const { code } = compileWithVBind(`<div :value.prop="foo" />`)
+    expect(code).matchSnapshot()
+    expect(code).contains('_setValue(n0, _ctx.foo)')
+  })
+
+  test('.prop modifier (shorthand) w/ value', () => {
+    const { code } = compileWithVBind(`<div .value="foo" />`)
+    expect(code).matchSnapshot()
+    expect(code).contains('_setValue(n0, _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)')
+  })
+
+  test('.prop modifier (shorthand) w/ progress value', () => {
+    const { code } = compileWithVBind(`<progress .value="foo" />`)
+    expect(code).matchSnapshot()
+    expect(code).contains('_setDOMProp(n0, "value", _ctx.foo)')
   })
 
   test('.attr modifier', () => {
@@ -499,7 +545,7 @@ describe('compiler v-bind', () => {
       },
     })
     expect(code).contains('renderEffect')
-    expect(code).contains('_setAttr(n0, "foo-bar", _ctx.id, true)')
+    expect(code).contains('_setAttr(n0, "foo-bar", _ctx.id)')
   })
 
   test('.attr modifier w/ no expression', () => {
@@ -524,7 +570,31 @@ describe('compiler v-bind', () => {
     })
 
     expect(code).contains('renderEffect')
-    expect(code).contains('_setAttr(n0, "foo-bar", _ctx.fooBar, true)')
+    expect(code).contains('_setAttr(n0, "foo-bar", _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)')
+  })
+
+  test('.attr modifier w/ textContent', () => {
+    const { code } = compileWithVBind(`<div :textContent.attr="foo" />`)
+    expect(code).matchSnapshot()
+    expect(code).contains('_setAttr(n0, "textContent", _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)')
+  })
+
+  test('.attr modifier w/ progress value', () => {
+    const { code } = compileWithVBind(`<progress :value.attr="foo" />`)
+    expect(code).matchSnapshot()
+    expect(code).contains('_setAttr(n0, "value", _ctx.foo)')
   })
 
   test('attributes must be set as attribute', () => {
@@ -561,11 +631,11 @@ describe('compiler v-bind', () => {
     `)
 
     expect(code).matchSnapshot()
-    expect(code).contains('_setDOMProp(n0, "id", _ctx.id, true)')
-    expect(code).contains('_setDOMProp(n0, "title", _ctx.title, true)')
-    expect(code).contains('_setDOMProp(n0, "lang", _ctx.lang, true)')
-    expect(code).contains('_setDOMProp(n0, "dir", _ctx.dir, true)')
-    expect(code).contains('_setDOMProp(n0, "tabindex", _ctx.tabindex, true)')
+    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)')
   })
 
   test('SVG global attributes should set as dom prop', () => {
@@ -574,9 +644,9 @@ describe('compiler v-bind', () => {
     `)
 
     expect(code).matchSnapshot()
-    expect(code).contains('_setDOMProp(n0, "id", _ctx.id, true)')
-    expect(code).contains('_setDOMProp(n0, "lang", _ctx.lang, true)')
-    expect(code).contains('_setDOMProp(n0, "tabindex", _ctx.tabindex, true)')
+    expect(code).contains('_setDOMProp(n0, "id", _ctx.id)')
+    expect(code).contains('_setDOMProp(n0, "lang", _ctx.lang)')
+    expect(code).contains('_setDOMProp(n0, "tabindex", _ctx.tabindex)')
   })
 
   test('MathML global attributes should set as dom prop', () => {
@@ -585,13 +655,43 @@ describe('compiler v-bind', () => {
     `)
 
     expect(code).matchSnapshot()
-    expect(code).contains('_setDOMProp(n0, "autofucus", _ctx.autofucus, true)')
-    expect(code).contains('_setDOMProp(n0, "dir", _ctx.dir, true)')
-    expect(code).contains(
-      '_setDOMProp(n0, "displaystyle", _ctx.displaystyle, true)',
-    )
-    expect(code).contains('_setDOMProp(n0, "mathcolor", _ctx.mathcolor, true)')
-    expect(code).contains('_setDOMProp(n0, "tabindex", _ctx.tabindex, true)')
+    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)')
+  })
+
+  test(':innerHTML', () => {
+    const { code } = compileWithVBind(`
+      <div :innerHTML="foo"/>
+    `)
+    expect(code).matchSnapshot()
+    expect(code).contains('_setHtml(n0, _ctx.foo)')
+  })
+
+  test(':textContext', () => {
+    const { code } = compileWithVBind(`
+      <div :textContent="foo"/>
+    `)
+    expect(code).matchSnapshot()
+    expect(code).contains('_setText(n0, _ctx.foo)')
+  })
+
+  test(':value', () => {
+    const { code } = compileWithVBind(`
+      <input :value="foo"/>
+    `)
+    expect(code).matchSnapshot()
+    expect(code).contains('_setValue(n0, _ctx.foo)')
+  })
+
+  test(':value w/ progress', () => {
+    const { code } = compileWithVBind(`
+      <progress :value="foo"/>
+    `)
+    expect(code).matchSnapshot()
+    expect(code).contains('_setDynamicProp(n0, "value", _ctx.foo)')
   })
 
   test('number value', () => {
index 295b177d6ee4cca99ebcffc4f7a74473ff20bd42..e30d4c7edefe1136a533a82201c78f5e331a41bf 100644 (file)
@@ -23,6 +23,7 @@ import {
 } from './utils'
 import {
   attributeCache,
+  canSetValueDirectly,
   isHTMLGlobalAttr,
   isHTMLTag,
   isMathMLGlobalAttr,
@@ -44,39 +45,7 @@ export function genSetProp(
     tag,
   } = oper
 
-  const keyName = key.content
-  const tagName = tag.toUpperCase()
-  const attrCacheKey = `${tagName}_${keyName}`
-
-  let helperName: VaporHelper
-  let omitKey = false
-  if (keyName === 'class') {
-    helperName = 'setClass'
-    omitKey = true
-  } else if (keyName === 'style') {
-    helperName = 'setStyle'
-    omitKey = true
-  } else if (modifier) {
-    helperName = modifier === '.' ? 'setDOMProp' : 'setAttr'
-  } else if (
-    attributeCache[attrCacheKey] === undefined
-      ? (attributeCache[attrCacheKey] = shouldSetAsAttr(
-          tag.toUpperCase(),
-          keyName,
-        ))
-      : attributeCache[attrCacheKey]
-  ) {
-    helperName = 'setAttr'
-  } else if (
-    (isHTMLTag(tag) && isHTMLGlobalAttr(keyName)) ||
-    (isSVGTag(tag) && isSvgGlobalAttr(keyName)) ||
-    (isMathMLTag(tag) && isMathMLGlobalAttr(keyName))
-  ) {
-    helperName = 'setDOMProp'
-  } else {
-    helperName = 'setDynamicProp'
-  }
-
+  const { helperName, omitKey } = getRuntimeHelper(tag, key.content, modifier)
   return [
     NEWLINE,
     ...genCall(
@@ -84,7 +53,10 @@ export function genSetProp(
       `n${oper.element}`,
       omitKey ? false : genExpression(key, context),
       genPropValue(values, context),
-      oper.root && 'true',
+      // only `setClass` and `setStyle` need merge inherit attr
+      oper.root && (helperName === 'setClass' || helperName === 'setStyle')
+        ? 'true'
+        : undefined,
     ),
   ]
 }
@@ -196,3 +168,70 @@ export function genSetInheritAttrs(
   if (value == null) return []
   return [NEWLINE, ...genCall(vaporHelper('setInheritAttrs'), value)]
 }
+
+function getRuntimeHelper(
+  tag: string,
+  keyName: string,
+  modifier: '.' | '^' | undefined,
+) {
+  const tagName = tag.toUpperCase()
+  let helperName: VaporHelper
+  let omitKey = false
+
+  if (modifier) {
+    if (modifier === '.') {
+      const helper = getSpecialHelper(keyName, tagName)
+      if (helper) {
+        helperName = helper.name
+        omitKey = helper.omitKey
+      } else {
+        helperName = 'setDOMProp'
+        omitKey = false
+      }
+    } else {
+      helperName = 'setAttr'
+    }
+  } else {
+    const attrCacheKey = `${tagName}_${keyName}`
+    const helper = getSpecialHelper(keyName, tagName)
+    if (helper) {
+      helperName = helper.name
+      omitKey = helper.omitKey
+    } else if (
+      attributeCache[attrCacheKey] === undefined
+        ? (attributeCache[attrCacheKey] = shouldSetAsAttr(tagName, keyName))
+        : attributeCache[attrCacheKey]
+    ) {
+      helperName = 'setAttr'
+    } else if (
+      (isHTMLTag(tag) && isHTMLGlobalAttr(keyName)) ||
+      (isSVGTag(tag) && isSvgGlobalAttr(keyName)) ||
+      (isMathMLTag(tag) && isMathMLGlobalAttr(keyName))
+    ) {
+      helperName = 'setDOMProp'
+    } else {
+      helperName = 'setDynamicProp'
+    }
+  }
+  return { helperName, omitKey }
+}
+
+const specialHelpers: Record<string, { name: VaporHelper; omitKey: boolean }> =
+  {
+    class: { name: 'setClass', omitKey: true },
+    style: { name: 'setStyle', omitKey: true },
+    innerHTML: { name: 'setHtml', omitKey: true },
+    textContent: { name: 'setText', omitKey: true },
+  }
+
+const getSpecialHelper = (
+  keyName: string,
+  tagName: string,
+): { name: VaporHelper; omitKey: boolean } | null => {
+  // special case for 'value' property
+  if (keyName === 'value' && canSetValueDirectly(tagName)) {
+    return { name: 'setValue', omitKey: true }
+  }
+
+  return specialHelpers[keyName] || null
+}
index 7ccb1f51ee3b37c0889aadaaf1dcda62967ca603..c5e526b35a347aa4ec731b959a44d22cacbc7459 100644 (file)
@@ -7,6 +7,7 @@ import {
   setDynamicProps,
   setHtml,
   setText,
+  setValue,
 } from '../../src/dom/prop'
 import { setStyle } from '../../src/dom/style'
 import {
@@ -239,43 +240,32 @@ describe('patchProp', () => {
     })
   })
 
-  describe('setDOMProp', () => {
-    test('should set DOM property', () => {
-      const el = document.createElement('div')
-      setDOMProp(el, 'textContent', null)
-      expect(el.textContent).toBe('')
-      setDOMProp(el, 'textContent', 'foo')
-      expect(el.textContent).toBe('foo')
-
-      setDOMProp(el, 'innerHTML', null)
-      expect(el.innerHTML).toBe('')
-      setDOMProp(el, 'innerHTML', '<p>bar</p>')
-      expect(el.innerHTML).toBe('<p>bar</p>')
-    })
-
+  describe('setValue', () => {
     test('should set value prop', () => {
       const el = document.createElement('input')
-      setDOMProp(el, 'value', 'foo')
+      setValue(el, 'foo')
       expect(el.value).toBe('foo')
-      setDOMProp(el, 'value', null)
+      setValue(el, null)
       expect(el.value).toBe('')
       expect(el.getAttribute('value')).toBe(null)
       const obj = {}
-      setDOMProp(el, 'value', obj)
+      setValue(el, obj)
       expect(el.value).toBe(obj.toString())
       expect((el as any)._value).toBe(obj)
 
       const option = document.createElement('option')
-      setDOMProp(option, 'textContent', 'foo')
+      setText(option, 'foo')
       expect(option.value).toBe('foo')
       expect(option.getAttribute('value')).toBe(null)
 
-      setDOMProp(option, 'value', 'bar')
+      setValue(option, 'bar')
       expect(option.textContent).toBe('foo')
       expect(option.value).toBe('bar')
       expect(option.getAttribute('value')).toBe('bar')
     })
+  })
 
+  describe('setDOMProp', () => {
     test('should be boolean prop', () => {
       const el = document.createElement('select')
       setDOMProp(el, 'multiple', '')
@@ -455,6 +445,8 @@ describe('patchProp', () => {
   describe('setText', () => {
     test('should set textContent', () => {
       const el = document.createElement('div')
+      setText(el, null)
+      expect(el.textContent).toBe('')
       setText(el, 'foo')
       expect(el.textContent).toBe('foo')
       setText(el, 'bar')
@@ -465,6 +457,8 @@ describe('patchProp', () => {
   describe('setHtml', () => {
     test('should set innerHTML', () => {
       const el = document.createElement('div')
+      setHtml(el, null)
+      expect(el.innerHTML).toBe('')
       setHtml(el, '<p>foo</p>')
       expect(el.innerHTML).toBe('<p>foo</p>')
       setHtml(el, '<p>bar</p>')
index 8dfe24875872b2c3e8edddafeea1cc9cec85c4bd..bd6ce1f12d8917a9e3eae9578f16a75c2631fdbd 100644 (file)
@@ -1,5 +1,6 @@
 import {
   attributeCache,
+  canSetValueDirectly,
   includeBooleanAttr,
   isArray,
   isFunction,
@@ -50,42 +51,28 @@ export function setAttr(el: Element, key: string, value: any): void {
   }
 }
 
-export function setDOMProp(el: any, key: string, value: any): void {
-  const oldVal = recordPropMetadata(el, key, value)
+export function setValue(el: any, value: any): void {
+  const oldVal = recordPropMetadata(el, 'value', value)
   if (value === oldVal) return
 
-  if (key === 'innerHTML' || key === 'textContent') {
-    // TODO special checks
-    // if (prevChildren) {
-    //   unmountChildren(prevChildren, parentComponent, parentSuspense)
-    // }
-    el[key] = value == null ? '' : value
-    return
+  // store value as _value as well since
+  // non-string values will be stringified.
+  el._value = value
+  // #4956: <option> value will fallback to its text content so we need to
+  // compare against its attribute value instead.
+  const oldValue = el.tagName === 'OPTION' ? el.getAttribute('value') : el.value
+  const newValue = value == null ? '' : value
+  if (oldValue !== newValue) {
+    el.value = newValue
   }
-
-  const tag = el.tagName
-
-  if (
-    key === 'value' &&
-    tag !== 'PROGRESS' &&
-    // custom elements may use _value internally
-    !tag.includes('-')
-  ) {
-    // store value as _value as well since
-    // non-string values will be stringified.
-    el._value = value
-    // #4956: <option> value will fallback to its text content so we need to
-    // compare against its attribute value instead.
-    const oldValue = tag === 'OPTION' ? el.getAttribute('value') : el.value
-    const newValue = value == null ? '' : value
-    if (oldValue !== newValue) {
-      el.value = newValue
-    }
-    if (value == null) {
-      el.removeAttribute(key)
-    }
-    return
+  if (value == null) {
+    el.removeAttribute('value')
   }
+}
+
+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) {
@@ -113,7 +100,7 @@ export function setDOMProp(el: any, key: string, value: any): void {
     // do not warn if value is auto-coerced from nullish values
     if (__DEV__ && !needRemove) {
       warn(
-        `Failed setting prop "${key}" on <${tag.toLowerCase()}>: ` +
+        `Failed setting prop "${key}" on <${el.tagName.toLowerCase()}>: ` +
           `value ${value} is invalid.`,
         e,
       )
@@ -138,6 +125,22 @@ export function setDynamicProp(el: Element, key: string, value: any): void {
         ? ((key = key.slice(1)), false)
         : shouldSetAsProp(el, key, value, isSVG)
   ) {
+    if (key === 'innerHTML') {
+      setHtml(el, value)
+      return
+    }
+
+    if (key === 'textContent') {
+      setText(el, value)
+      return
+    }
+
+    const tag = el.tagName
+    if (key === 'value' && canSetValueDirectly(tag)) {
+      setValue(el, value)
+      return
+    }
+
     setDOMProp(el, key, value)
   } else {
     // TODO special case for <input v-model type="checkbox">
@@ -220,7 +223,7 @@ export function setText(el: Node, ...values: any[]): void {
 export function setHtml(el: Element, value: any): void {
   const oldVal = recordPropMetadata(el, 'innerHTML', value)
   if (value !== oldVal) {
-    el.innerHTML = value
+    el.innerHTML = value == null ? '' : value
   }
 }
 
index 0cfd2c798fe724ebcebc6ad5d6cb9f4b3dd14c97..c03548be334492462b2269779c048eb3ad34506f 100644 (file)
@@ -113,6 +113,7 @@ export {
   setHtml,
   setClass,
   setAttr,
+  setValue,
   setDOMProp,
   setDynamicProp,
   setDynamicProps,
index 158b893bc41e450c470c111f3e7c55dc628f80e7..55c34ffe6c2b3d5e34658135b89dad1701e15afe 100644 (file)
@@ -227,3 +227,11 @@ export function genCacheKey(source: string, options: any): string {
     )
   )
 }
+
+export function canSetValueDirectly(tagName: string): boolean {
+  return (
+    tagName !== 'PROGRESS' &&
+    // custom elements may use _value internally
+    !tagName.includes('-')
+  )
+}