]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
perf(vapor): optimize text bindings and v-text
authorEvan You <evan@vuejs.org>
Tue, 11 Feb 2025 10:39:27 +0000 (18:39 +0800)
committerEvan You <evan@vuejs.org>
Tue, 11 Feb 2025 10:39:27 +0000 (18:39 +0800)
36 files changed:
packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformText.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vText.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/transformText.spec.ts
packages/compiler-vapor/__tests__/transforms/vFor.spec.ts
packages/compiler-vapor/__tests__/transforms/vIf.spec.ts
packages/compiler-vapor/__tests__/transforms/vOnce.spec.ts
packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts
packages/compiler-vapor/__tests__/transforms/vText.spec.ts
packages/compiler-vapor/src/generators/operation.ts
packages/compiler-vapor/src/generators/template.ts
packages/compiler-vapor/src/generators/text.ts
packages/compiler-vapor/src/ir/index.ts
packages/compiler-vapor/src/transform.ts
packages/compiler-vapor/src/transforms/transformText.ts
packages/compiler-vapor/src/transforms/vText.ts
packages/compiler-vapor/src/utils.ts
packages/runtime-vapor/__tests__/apiCreateVaporApp.spec.ts
packages/runtime-vapor/__tests__/apiInject.spec.ts
packages/runtime-vapor/__tests__/apiLifecycle.spec.ts
packages/runtime-vapor/__tests__/apiSetupContext.spec.ts
packages/runtime-vapor/__tests__/component.spec.ts
packages/runtime-vapor/__tests__/componentAttrs.spec.ts
packages/runtime-vapor/__tests__/componentProps.spec.ts
packages/runtime-vapor/__tests__/componentSlots.spec.ts
packages/runtime-vapor/__tests__/dom/prop.spec.ts
packages/runtime-vapor/__tests__/dom/templateRef.spec.ts
packages/runtime-vapor/__tests__/if.spec.ts
packages/runtime-vapor/src/dom/node.ts
packages/runtime-vapor/src/dom/prop.ts

index c8c9d254d62d85d24b784d06f5564e08708d1724..323b9df78447c4da3b49d223b86d763e50ce5466 100644 (file)
@@ -1,12 +1,13 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`compile > bindings 1`] = `
-"import { setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
-const t0 = _template("<div></div>", true)
+"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template("<div> </div>", true)
 
 export function render(_ctx, $props, $emit, $attrs, $slots) {
   const n0 = t0()
-  _renderEffect(() => _setText(n0, "count is ", _ctx.count, "."))
+  const x0 = _child(n0)
+  _renderEffect(() => _setText(x0, "count is " + _toDisplayString(_ctx.count) + "."))
   return n0
 }"
 `;
@@ -148,7 +149,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, createComponentWithFallback as _createComponentWithFallback, createTextNode as _createTextNode, insert as _insert, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
+"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, createTextNode as _createTextNode, insert as _insert, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
 const t0 = _template("<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div>")
 const t1 = _template("<div></div>")
 
@@ -157,33 +158,37 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
   const n0 = t0()
   const n3 = t1()
   const n1 = _createComponentWithFallback(_component_Comp)
-  const n2 = _createTextNode(() => [_ctx.bar])
+  const n2 = _createTextNode()
   _insert([n1, n2], n3)
-  _renderEffect(() => _setProp(n3, "id", _ctx.foo))
+  _renderEffect(() => {
+    _setText(n2, _toDisplayString(_ctx.bar))
+    _setProp(n3, "id", _ctx.foo)
+  })
   return [n0, n3]
 }"
 `;
 
 exports[`compile > dynamic root 1`] = `
-"import { createTextNode as _createTextNode } from 'vue';
+"import { createTextNode as _createTextNode, toDisplayString as _toDisplayString } from 'vue';
 
 export function render(_ctx) {
-  const n0 = _createTextNode(() => [1, 2])
+  const n0 = _createTextNode(_toDisplayString(1) + _toDisplayString(2))
   return n0
 }"
 `;
 
 exports[`compile > dynamic root nodes and interpolation 1`] = `
-"import { setText as _setText, setProp as _setProp, renderEffect as _renderEffect, delegateEvents as _delegateEvents, template as _template } from 'vue';
-const t0 = _template("<button></button>", true)
+"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, delegateEvents as _delegateEvents, template as _template } from 'vue';
+const t0 = _template("<button> </button>", true)
 _delegateEvents("click")
 
 export function render(_ctx) {
   const n0 = t0()
+  const x0 = _child(n0)
   n0.$evtclick = e => _ctx.handleClick(e)
   _renderEffect(() => {
     const _count = _ctx.count
-    _setText(n0, _count, "foo", _count, "foo", _count)
+    _setText(x0, _toDisplayString(_count) + "foo" + _toDisplayString(_count) + "foo" + _toDisplayString(_count))
     _setProp(n0, "id", _count)
   })
   return n0
@@ -192,7 +197,8 @@ export function render(_ctx) {
 
 exports[`compile > expression parsing > interpolation 1`] = `
 "
-  const n0 = _createTextNode(() => [a + b.value])
+  const n0 = _createTextNode()
+  _renderEffect(() => _setText(n0, _toDisplayString(a + b.value)))
   return n0
 "
 `;
@@ -223,10 +229,10 @@ export function render(_ctx) {
 `;
 
 exports[`compile > static + dynamic root 1`] = `
-"import { createTextNode as _createTextNode } from 'vue';
+"import { createTextNode as _createTextNode, toDisplayString as _toDisplayString } from 'vue';
 
 export function render(_ctx) {
-  const n0 = _createTextNode(() => [1, 2, "3", 4, 5, "6", 7, 8, "9", 'A', 'B'])
+  const n0 = _createTextNode(_toDisplayString(1) + _toDisplayString(2) + "3" + _toDisplayString(4) + _toDisplayString(5) + "6" + _toDisplayString(7) + _toDisplayString(8) + "9" + 'A' + 'B')
   return n0
 }"
 `;
index 53e0ed772c5757b9370db580e33deb8e7413b512..ec449cbd2d34eea4961a473d678819520499f1c4 100644 (file)
@@ -1,28 +1,31 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`compiler: expression > basic 1`] = `
-"import { createTextNode as _createTextNode } from 'vue';
+"import { createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect } from 'vue';
 
 export function render(_ctx) {
-  const n0 = _createTextNode(() => [_ctx.a])
+  const n0 = _createTextNode()
+  _renderEffect(() => _setText(n0, _toDisplayString(_ctx.a)))
   return n0
 }"
 `;
 
 exports[`compiler: expression > props 1`] = `
-"import { createTextNode as _createTextNode } from 'vue';
+"import { createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect } from 'vue';
 
 export function render(_ctx, $props, $emit, $attrs, $slots) {
-  const n0 = _createTextNode(() => [$props.foo])
+  const n0 = _createTextNode()
+  _renderEffect(() => _setText(n0, _toDisplayString($props.foo)))
   return n0
 }"
 `;
 
 exports[`compiler: expression > props aliased 1`] = `
-"import { createTextNode as _createTextNode } from 'vue';
+"import { createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect } from 'vue';
 
 export function render(_ctx, $props, $emit, $attrs, $slots) {
-  const n0 = _createTextNode(() => [$props['bar']])
+  const n0 = _createTextNode()
+  _renderEffect(() => _setText(n0, _toDisplayString($props['bar'])))
   return n0
 }"
 `;
index f8c5da21bc0b6a28e9813b45e683c71a1d681eb2..373b0795bb1c87f61425cb008dd0a75462ccbc5a 100644 (file)
@@ -1,42 +1,48 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`compiler: children transform > children & sibling references 1`] = `
-"import { child as _child, nextn as _nextn, next as _next, createTextNode as _createTextNode, insert as _insert, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
-const t0 = _template("<div><p></p> <!><p></p></div>", true)
+"import { child as _child, nextn as _nextn, next as _next, createTextNode as _createTextNode, insert as _insert, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template("<div><p> </p> <!><p> </p></div>", true)
 
 export function render(_ctx) {
   const n4 = t0()
   const n0 = _child(n4)
   const n3 = _nextn(n0, 2)
   const n2 = _next(n3)
-  const n1 = _createTextNode(() => [_ctx.second, " ", _ctx.third, " "])
+  const x0 = _child(n0)
+  const n1 = _createTextNode()
+  const x2 = _child(n2)
   _insert(n1, n4, n3)
   _renderEffect(() => {
-    _setText(n0, _ctx.first)
-    _setText(n2, _ctx.forth)
+    _setText(x0, _toDisplayString(_ctx.first))
+    _setText(n1, _toDisplayString(_ctx.second) + " " + _toDisplayString(_ctx.third) + " ")
+    _setText(x2, _toDisplayString(_ctx.forth))
   })
   return n4
 }"
 `;
 
 exports[`compiler: children transform > efficient traversal 1`] = `
-"import { child as _child, next as _next, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
-const t0 = _template("<div><div>x</div><div><span></span></div><div><span></span></div><div><span></span></div></div>", true)
+"import { child as _child, next as _next, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template("<div><div>x</div><div><span> </span></div><div><span> </span></div><div><span> </span></div></div>", true)
 
 export function render(_ctx) {
   const n3 = t0()
-  const _n0 = _next(_child(n3))
-  const n0 = _child(_n0)
-  const _n1 = _next(_n0)
-  const n1 = _child(_n1)
-  const _n2 = _next(_n1)
-  const n2 = _child(_n2)
+  const p0 = _next(_child(n3))
+  const n0 = _child(p0)
+  const p1 = _next(p0)
+  const n1 = _child(p1)
+  const p2 = _next(p1)
+  const n2 = _child(p2)
+  const x0 = _child(n0)
+  const x1 = _child(n1)
+  const x2 = _child(n2)
   _renderEffect(() => {
     const _msg = _ctx.msg
     
-    _setText(n0, _msg)
-    _setText(n1, _msg)
-    _setText(n2, _msg)
+    _setText(x0, _toDisplayString(_msg))
+    _setText(x1, _toDisplayString(_msg))
+    _setText(x2, _toDisplayString(_msg))
   })
   return n3
 }"
index cdc6984834cd421548d7a9346c51ae0a652bb623..706b44c9a3eda646fa48988f687f4f9a6625aa18 100644 (file)
@@ -1,10 +1,11 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`compiler: text transform > consecutive text 1`] = `
-"import { createTextNode as _createTextNode } from 'vue';
+"import { createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect } from 'vue';
 
 export function render(_ctx) {
-  const n0 = _createTextNode(() => [_ctx.msg])
+  const n0 = _createTextNode()
+  _renderEffect(() => _setText(n0, _toDisplayString(_ctx.msg)))
   return n0
 }"
 `;
@@ -13,7 +14,7 @@ exports[`compiler: text transform > no consecutive text 1`] = `
 "import { createTextNode as _createTextNode } from 'vue';
 
 export function render(_ctx) {
-  const n0 = _createTextNode(["hello world"])
+  const n0 = _createTextNode("hello world")
   return n0
 }"
 `;
index 183a3b726c16ec0aa99b393b7ec3c0f262612fd4..d7e763e849cb7a02dc70c019e094e3fd8c385387 100644 (file)
@@ -1,13 +1,14 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`compiler: v-for > array de-structured value (with rest) 1`] = `
-"import { setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
-const t0 = _template("<div></div>", true)
+"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
+const t0 = _template("<div> </div>", true)
 
 export function render(_ctx) {
   const n0 = _createFor(() => (_ctx.list), (_for_item0, _for_key0) => {
     const n2 = t0()
-    _renderEffect(() => _setText(n2, _for_item0.value[0] + _for_item0.value.slice(1) + _for_key0.value))
+    const x2 = _child(n2)
+    _renderEffect(() => _setText(x2, _toDisplayString(_for_item0.value[0] + _for_item0.value.slice(1) + _for_key0.value)))
     return n2
   }, ([id, ...other], index) => (id))
   return n0
@@ -15,13 +16,14 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-for > array de-structured value 1`] = `
-"import { setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
-const t0 = _template("<div></div>", true)
+"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
+const t0 = _template("<div> </div>", true)
 
 export function render(_ctx) {
   const n0 = _createFor(() => (_ctx.list), (_for_item0, _for_key0) => {
     const n2 = t0()
-    _renderEffect(() => _setText(n2, _for_item0.value[0] + _for_item0.value[1] + _for_key0.value))
+    const x2 = _child(n2)
+    _renderEffect(() => _setText(x2, _toDisplayString(_for_item0.value[0] + _for_item0.value[1] + _for_key0.value)))
     return n2
   }, ([id, other], index) => (id))
   return n0
@@ -29,15 +31,16 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-for > basic v-for 1`] = `
-"import { setText as _setText, renderEffect as _renderEffect, createFor as _createFor, delegateEvents as _delegateEvents, template as _template } from 'vue';
-const t0 = _template("<div></div>", true)
+"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, delegateEvents as _delegateEvents, template as _template } from 'vue';
+const t0 = _template("<div> </div>", true)
 _delegateEvents("click")
 
 export function render(_ctx) {
   const n0 = _createFor(() => (_ctx.items), (_for_item0) => {
     const n2 = t0()
+    const x2 = _child(n2)
     n2.$evtclick = () => (_ctx.remove(_for_item0.value))
-    _renderEffect(() => _setText(n2, _for_item0.value))
+    _renderEffect(() => _setText(x2, _toDisplayString(_for_item0.value)))
     return n2
   }, (item) => (item.id))
   return n0
@@ -62,8 +65,8 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-for > nested v-for 1`] = `
-"import { setText as _setText, renderEffect as _renderEffect, createFor as _createFor, insert as _insert, template as _template } from 'vue';
-const t0 = _template("<span></span>")
+"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, insert as _insert, template as _template } from 'vue';
+const t0 = _template("<span> </span>")
 const t1 = _template("<div></div>", true)
 
 export function render(_ctx) {
@@ -71,7 +74,8 @@ export function render(_ctx) {
     const n5 = t1()
     const n2 = _createFor(() => (_for_item0.value), (_for_item1) => {
       const n4 = t0()
-      _renderEffect(() => _setText(n4, _for_item1.value+_for_item0.value))
+      const x4 = _child(n4)
+      _renderEffect(() => _setText(x4, _toDisplayString(_for_item1.value+_for_item0.value)))
       return n4
     }, null, null, null, true)
     _insert(n2, n5)
@@ -82,13 +86,14 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-for > object de-structured value (with rest) 1`] = `
-"import { getRestElement as _getRestElement, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
-const t0 = _template("<div></div>", true)
+"import { getRestElement as _getRestElement, child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
+const t0 = _template("<div> </div>", true)
 
 export function render(_ctx) {
   const n0 = _createFor(() => (_ctx.list), (_for_item0, _for_key0) => {
     const n2 = t0()
-    _renderEffect(() => _setText(n2, _for_item0.value.id + _getRestElement(_for_item0.value, ["id"]) + _for_key0.value))
+    const x2 = _child(n2)
+    _renderEffect(() => _setText(x2, _toDisplayString(_for_item0.value.id + _getRestElement(_for_item0.value, ["id"]) + _for_key0.value)))
     return n2
   }, ({ id, ...other }, index) => (id))
   return n0
@@ -96,13 +101,14 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-for > object de-structured value 1`] = `
-"import { setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
-const t0 = _template("<span></span>", true)
+"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
+const t0 = _template("<span> </span>", true)
 
 export function render(_ctx) {
   const n0 = _createFor(() => (_ctx.items), (_for_item0) => {
     const n2 = t0()
-    _renderEffect(() => _setText(n2, _for_item0.value.id, _for_item0.value.value))
+    const x2 = _child(n2)
+    _renderEffect(() => _setText(x2, _toDisplayString(_for_item0.value.id) + _toDisplayString(_for_item0.value.value)))
     return n2
   }, ({ id, value }) => (id))
   return n0
@@ -110,13 +116,14 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-for > v-for aliases w/ complex expressions 1`] = `
-"import { getDefaultValue as _getDefaultValue, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
-const t0 = _template("<div></div>", true)
+"import { getDefaultValue as _getDefaultValue, child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
+const t0 = _template("<div> </div>", true)
 
 export function render(_ctx) {
   const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
     const n2 = t0()
-    _renderEffect(() => _setText(n2, _getDefaultValue(_for_item0.value.foo, _ctx.bar) + _ctx.bar + _ctx.baz + _getDefaultValue(_for_item0.value.baz[0], _ctx.quux) + _ctx.quux))
+    const x2 = _child(n2)
+    _renderEffect(() => _setText(x2, _toDisplayString(_getDefaultValue(_for_item0.value.foo, _ctx.bar) + _ctx.bar + _ctx.baz + _getDefaultValue(_for_item0.value.baz[0], _ctx.quux) + _ctx.quux)))
     return n2
   })
   return n0
index a01ec4774d5e778e151e2aa367f12964eaad59f9..360823cdcf69ac45dfa7b03e89a001e283f28d8a 100644 (file)
@@ -1,13 +1,14 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`compiler: v-if > basic v-if 1`] = `
-"import { setText as _setText, renderEffect as _renderEffect, createIf as _createIf, template as _template } from 'vue';
-const t0 = _template("<div></div>", true)
+"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createIf as _createIf, template as _template } from 'vue';
+const t0 = _template("<div> </div>", true)
 
 export function render(_ctx) {
   const n0 = _createIf(() => (_ctx.ok), () => {
     const n2 = t0()
-    _renderEffect(() => _setText(n2, _ctx.msg))
+    const x2 = _child(n2)
+    _renderEffect(() => _setText(x2, _toDisplayString(_ctx.msg)))
     return n2
   })
   return n0
@@ -15,13 +16,13 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-if > comment between branches 1`] = `
-"import { createIf as _createIf, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
+"import { createIf as _createIf, child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
 const t0 = _template("<div></div>")
 const t1 = _template("<!--foo-->")
 const t2 = _template("<p></p>")
 const t3 = _template("<!--bar-->")
 const t4 = _template("fine")
-const t5 = _template("<input>")
+const t5 = _template("<div> </div>")
 
 export function render(_ctx) {
   const n13 = t5()
@@ -37,7 +38,8 @@ export function render(_ctx) {
     const n11 = t4()
     return [n10, n11]
   }))
-  _renderEffect(() => _setText(n13, _ctx.text))
+  const x13 = _child(n13)
+  _renderEffect(() => _setText(x13, _toDisplayString(_ctx.text)))
   return [n0, n13]
 }"
 `;
@@ -60,17 +62,18 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-if > template v-if 1`] = `
-"import { setText as _setText, renderEffect as _renderEffect, createIf as _createIf, template as _template } from 'vue';
+"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createIf as _createIf, template as _template } from 'vue';
 const t0 = _template("<div></div>")
 const t1 = _template("hello")
-const t2 = _template("<p></p>", true)
+const t2 = _template("<p> </p>", true)
 
 export function render(_ctx) {
   const n0 = _createIf(() => (_ctx.ok), () => {
     const n2 = t0()
     const n3 = t1()
     const n4 = t2()
-    _renderEffect(() => _setText(n4, _ctx.msg))
+    const x4 = _child(n4)
+    _renderEffect(() => _setText(x4, _toDisplayString(_ctx.msg)))
     return [n2, n3, n4]
   })
   return n0
index 15abf38f9992e4a37153d391b5ba9e5d3634e19f..94fd139589bbbfd146ee3a548152511e8fc42c53 100644 (file)
@@ -12,13 +12,13 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-once > basic 1`] = `
-"import { child as _child, createTextNode as _createTextNode, setClass as _setClass, prepend as _prepend, template as _template } from 'vue';
+"import { child as _child, createTextNode as _createTextNode, toDisplayString as _toDisplayString, setClass as _setClass, prepend as _prepend, template as _template } from 'vue';
 const t0 = _template("<div><span></span></div>", true)
 
 export function render(_ctx, $props, $emit, $attrs, $slots) {
   const n2 = t0()
   const n1 = _child(n2)
-  const n0 = _createTextNode([_ctx.msg, " "])
+  const n0 = _createTextNode(_toDisplayString(_ctx.msg) + " ")
   _setClass(n1, _ctx.clz)
   _prepend(n2, n0)
   return n2
index 334efa74557f7746000b069780751fd9e69fef71..bab5c1046059564cdca1eb74996e7ce805f0bca8 100644 (file)
@@ -22,7 +22,7 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: transform slot > dynamic slots name w/ v-for 1`] = `
-"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, createForSlots as _createForSlots, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createForSlots as _createForSlots, createComponentWithFallback as _createComponentWithFallback } from 'vue';
 
 export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
@@ -31,7 +31,8 @@ export function render(_ctx) {
       () => (_createForSlots(_ctx.list, (item) => ({
         name: item, 
         fn: (_slotProps0) => {
-          const n0 = _createTextNode(() => [_slotProps0["bar"]])
+          const n0 = _createTextNode()
+          _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["bar"])))
           return n0
         }
       })))
@@ -157,7 +158,7 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: transform slot > nested slots scoping 1`] = `
-"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
 const t0 = _template(" ")
 
 export function render(_ctx) {
@@ -168,11 +169,13 @@ export function render(_ctx) {
       const n2 = t0()
       const n1 = _createComponentWithFallback(_component_Inner, null, {
         "default": (_slotProps1) => {
-          const n0 = _createTextNode(() => [_slotProps0["foo"] + _slotProps1["bar"] + _ctx.baz])
+          const n0 = _createTextNode()
+          _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _slotProps1["bar"] + _ctx.baz)))
           return n0
         }
       })
-      const n3 = _createTextNode(() => [_slotProps0["foo"] + _ctx.bar + _ctx.baz])
+      const n3 = _createTextNode()
+      _renderEffect(() => _setText(n3, _toDisplayString(_slotProps0["foo"] + _ctx.bar + _ctx.baz)))
       return [n1, n2, n3]
     }
   }, true)
@@ -181,7 +184,7 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: transform slot > on component dynamically named slot 1`] = `
-"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback } from 'vue';
 
 export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
@@ -190,7 +193,8 @@ export function render(_ctx) {
       () => ({
         name: _ctx.named, 
         fn: (_slotProps0) => {
-          const n0 = _createTextNode(() => [_slotProps0["foo"] + _ctx.bar])
+          const n0 = _createTextNode()
+          _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _ctx.bar)))
           return n0
         }
       })
@@ -201,13 +205,14 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: transform slot > on component named slot 1`] = `
-"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback } from 'vue';
 
 export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
   const n1 = _createComponentWithFallback(_component_Comp, null, {
     "named": (_slotProps0) => {
-      const n0 = _createTextNode(() => [_slotProps0["foo"] + _ctx.bar])
+      const n0 = _createTextNode()
+      _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _ctx.bar)))
       return n0
     }
   }, true)
@@ -216,13 +221,14 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: transform slot > on-component default slot 1`] = `
-"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback } from 'vue';
 
 export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
   const n1 = _createComponentWithFallback(_component_Comp, null, {
     "default": (_slotProps0) => {
-      const n0 = _createTextNode(() => [_slotProps0["foo"] + _ctx.bar])
+      const n0 = _createTextNode()
+      _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _ctx.bar)))
       return n0
     }
   }, true)
index 5ae5b2a7413872241fdf78821cccc6e48300b83e..9a3b88acba308231b58f1d2eb1b31ed4de2adae4 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 { setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
-const t0 = _template("<div></div>", true)
+exports[`v-text > should convert v-text to setText 1`] = `
+"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template("<div> </div>", true)
 
 export function render(_ctx, $props, $emit, $attrs, $slots) {
   const n0 = t0()
-  _renderEffect(() => _setText(n0, _ctx.str))
+  const x0 = _child(n0)
+  _renderEffect(() => _setText(x0, _toDisplayString(_ctx.str)))
   return n0
 }"
 `;
 
 exports[`v-text > should raise error and ignore children when v-text is present 1`] = `
-"import { setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
-const t0 = _template("<div></div>", true)
+"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template("<div> </div>", true)
 
 export function render(_ctx) {
   const n0 = t0()
-  _renderEffect(() => _setText(n0, _ctx.test))
+  const x0 = _child(n0)
+  _renderEffect(() => _setText(x0, _toDisplayString(_ctx.test)))
   return n0
 }"
 `;
index 96f4d8546201cfc53f69271e4640e09cff3ceb5a..a668c76258ff49d55788617e0997a2f18a00c637 100644 (file)
@@ -36,7 +36,6 @@ describe('compiler: text transform', () => {
             isStatic: false,
           },
         ],
-        effect: false,
       },
     ])
   })
@@ -49,15 +48,9 @@ describe('compiler: text transform', () => {
       {
         type: IRNodeTypes.CREATE_TEXT_NODE,
         id: 0,
-        values: [
-          {
-            type: IRNodeTypes.SET_TEXT,
-            content: 'msg',
-            isStatic: false,
-          },
-        ],
-        effect: true,
+        values: undefined,
       },
     ])
+    expect(ir.block.effect.length).toBe(1)
   })
 })
index 22ded75aeb98a3a2e174cd1a124950fe5c019007..31a6da36908193e09452e20476a5929a273a8bc2 100644 (file)
@@ -32,7 +32,7 @@ describe('compiler: v-for', () => {
 
     expect(code).matchSnapshot()
     expect(helpers).contains('createFor')
-    expect(ir.template).toEqual(['<div></div>'])
+    expect(ir.template).toEqual(['<div> </div>'])
     expect(ir.block.operation).toMatchObject([
       {
         type: IRNodeTypes.FOR,
@@ -89,7 +89,7 @@ describe('compiler: v-for', () => {
       `_createFor(() => (_for_item0.value), (_for_item1) => {`,
     )
     expect(code).contains(`_for_item1.value+_for_item0.value`)
-    expect(ir.template).toEqual(['<span></span>', '<div></div>'])
+    expect(ir.template).toEqual(['<span> </span>', '<div></div>'])
     expect(ir.block.operation).toMatchObject([
       {
         type: IRNodeTypes.FOR,
index bac95e0d217e4f7e6d0bb3e147b061b2334df971..8f2d572d0b75ac648a0f14ae6a94d6756873e75e 100644 (file)
@@ -32,7 +32,7 @@ describe('compiler: v-if', () => {
 
     expect(helpers).contains('createIf')
 
-    expect(ir.template).toEqual(['<div></div>'])
+    expect(ir.template).toEqual(['<div> </div>'])
     expect(ir.block.operation).toMatchObject([
       {
         type: IRNodeTypes.IF,
@@ -68,7 +68,7 @@ describe('compiler: v-if', () => {
     )
     expect(code).matchSnapshot()
 
-    expect(ir.template).toEqual(['<div></div>', 'hello', '<p></p>'])
+    expect(ir.template).toEqual(['<div></div>', 'hello', '<p> </p>'])
     expect(ir.block.effect).toEqual([])
     expect((ir.block.operation[0] as IfIRNode).positive.effect).toMatchObject([
       {
@@ -227,7 +227,7 @@ describe('compiler: v-if', () => {
       <p v-else-if="orNot"/>
       <!--bar-->
       <template v-else>fine</template>
-      <input v-text="text" />
+      <div v-text="text" />
     `)
     expect(code).matchSnapshot()
     expect(ir.template).toEqual([
@@ -237,7 +237,7 @@ describe('compiler: v-if', () => {
       '<!--bar-->',
       'fine',
 
-      '<input>',
+      '<div> </div>',
     ])
   })
 
index 1f40424e5f7ffca59f038dc6eecc785c128d707e..228350486bffa3e8cd64145e81c2a1147408df37 100644 (file)
@@ -42,7 +42,6 @@ describe('compiler: v-once', () => {
             isStatic: true,
           },
         ],
-        effect: false,
       },
       {
         element: 1,
index b3b1e6b19a9032f6b900dbb8fffa82a4a9da009f..caa45681a24ad23fdca24aee403c677ca18ed14b 100644 (file)
@@ -297,7 +297,7 @@ describe('compiler: transform slot', () => {
     expect(code).toMatchSnapshot()
 
     expect(code).contains(`fn: (_slotProps0) =>`)
-    expect(code).contains(`_createTextNode(() => [_slotProps0["bar"]])`)
+    expect(code).contains(`_setText(n0, _toDisplayString(_slotProps0["bar"]))`)
 
     expect(ir.block.operation[0].type).toBe(IRNodeTypes.CREATE_COMPONENT_NODE)
     expect(ir.block.operation).toMatchObject([
index 9a45b1eec09a0f0fda22ac366dc31f871456f097..4f074fee87e16b8a2b1d15e468028d7893125dcd 100644 (file)
@@ -15,7 +15,7 @@ const compileWithVText = makeCompile({
 })
 
 describe('v-text', () => {
-  test('should convert v-text to textContent', () => {
+  test('should convert v-text to setText', () => {
     const { code, ir, helpers } = compileWithVText(`<div v-text="str"></div>`, {
       bindingMetadata: {
         str: BindingTypes.SETUP_REF,
@@ -23,7 +23,12 @@ describe('v-text', () => {
     })
 
     expect(helpers).contains('setText')
-    expect(ir.block.operation).toMatchObject([])
+    expect(ir.block.operation).toMatchObject([
+      {
+        type: IRNodeTypes.GET_TEXT_CHILD,
+        parent: 0,
+      },
+    ])
 
     expect(ir.block.effect).toMatchObject([
       {
@@ -63,7 +68,7 @@ describe('v-text', () => {
     ])
 
     // children should have been removed
-    expect(ir.template).toEqual(['<div></div>'])
+    expect(ir.template).toEqual(['<div> </div>'])
 
     expect(ir.block.effect).toMatchObject([
       {
@@ -92,7 +97,7 @@ describe('v-text', () => {
 
     expect(code).matchSnapshot()
     // children should have been removed
-    expect(code).contains('template("<div></div>", true)')
+    expect(code).contains('template("<div> </div>", true)')
   })
 
   test('should raise error if has no expression', () => {
index 997b2655ec10c9bef0ddaaf8b7f59de7bc3e6db5..93eff05846d580bc47afe8324b5486b9ec8ed1c3 100644 (file)
@@ -7,7 +7,7 @@ import { genSetHtml } from './html'
 import { genIf } from './if'
 import { genDynamicProps, genSetProp } from './prop'
 import { genDeclareOldRef, genSetTemplateRef } from './templateRef'
-import { genCreateTextNode, genSetText } from './text'
+import { genCreateTextNode, genGetTextChild, genSetText } from './text'
 import {
   type CodeFragment,
   INDENT_END,
@@ -68,6 +68,8 @@ export function genOperation(
       return genSlotOutlet(oper, context)
     case IRNodeTypes.DIRECTIVE:
       return genBuiltinDirective(oper, context)
+    case IRNodeTypes.GET_TEXT_CHILD:
+      return genGetTextChild(oper, context)
     default:
       const exhaustiveCheck: never = oper
       throw new Error(
index bc94a8dbfb290ed2a447e3a94a229c081cba2f2b..68d4c20b3e46937fef531d340cc7489dbb21fbcb 100644 (file)
@@ -60,7 +60,9 @@ export function genChildren(
     const elementIndex = Number(index) + offset
     const newPath = [...path, elementIndex]
 
-    const variable = id === undefined ? `_n${context.block.tempId++}` : `n${id}`
+    // p for "placeholder" variables that are meant for possible reuse by
+    // other access paths
+    const variable = id === undefined ? `p${context.block.tempId++}` : `n${id}`
     push(NEWLINE, `const ${variable} = `)
 
     if (prev) {
index 8d8b849c80ac7df5659787bcfccc1f1656543c82..3c9835f88b8318f9049bf6f53347902c054f2972 100644 (file)
@@ -1,22 +1,25 @@
+import type { SimpleExpressionNode } from '@vue/compiler-dom'
 import type { CodegenContext } from '../generate'
-import type { CreateTextNodeIRNode, SetTextIRNode } from '../ir'
+import type {
+  CreateTextNodeIRNode,
+  GetTextChildIRNode,
+  SetTextIRNode,
+} from '../ir'
+import { getLiteralExpressionValue } from '../utils'
 import { genExpression } from './expression'
-import {
-  type CodeFragment,
-  DELIMITERS_ARRAY,
-  NEWLINE,
-  genCall,
-  genMulti,
-} from './utils'
+import { type CodeFragment, NEWLINE, genCall } from './utils'
 
 export function genSetText(
   oper: SetTextIRNode,
   context: CodegenContext,
 ): CodeFragment[] {
   const { helper } = context
-  const { element, values } = oper
-  const texts = values.map(value => genExpression(value, context))
-  return [NEWLINE, ...genCall(helper('setText'), `n${element}`, ...texts)]
+  const { element, values, generated } = oper
+  const texts = combineValues(values, context)
+  return [
+    NEWLINE,
+    ...genCall(helper('setText'), `${generated ? 'x' : 'n'}${element}`, texts),
+  ]
 }
 
 export function genCreateTextNode(
@@ -24,16 +27,40 @@ export function genCreateTextNode(
   context: CodegenContext,
 ): CodeFragment[] {
   const { helper } = context
-  const { id, values, effect } = oper
+  const { id, values } = oper
   return [
     NEWLINE,
     `const n${id} = `,
-    ...genCall(helper('createTextNode'), [
-      effect && '() => ',
-      ...genMulti(
-        DELIMITERS_ARRAY,
-        ...values.map(value => genExpression(value, context)),
-      ),
-    ]),
+    ...genCall(
+      helper('createTextNode'),
+      values && combineValues(values, context),
+    ),
+  ]
+}
+
+function combineValues(
+  values: SimpleExpressionNode[],
+  context: CodegenContext,
+): CodeFragment[] {
+  return values.flatMap((value, i) => {
+    let exp = genExpression(value, context)
+    if (getLiteralExpressionValue(value) == null) {
+      // dynamic, wrap with toDisplayString
+      exp = genCall(context.helper('toDisplayString'), exp)
+    }
+    if (i > 0) {
+      exp.unshift(' + ')
+    }
+    return exp
+  })
+}
+
+export function genGetTextChild(
+  oper: GetTextChildIRNode,
+  context: CodegenContext,
+): CodeFragment[] {
+  return [
+    NEWLINE,
+    `const x${oper.parent} = ${context.helper('child')}(n${oper.parent})`,
   ]
 }
index 395a5c964ecdfb6a0caeb626395570f418db50b1..1509d37424cd12afd35e06dc222214d25b497441 100644 (file)
@@ -34,6 +34,8 @@ export enum IRNodeTypes {
 
   IF,
   FOR,
+
+  GET_TEXT_CHILD,
 }
 
 export interface BaseIRNode {
@@ -118,6 +120,7 @@ export interface SetTextIRNode extends BaseIRNode {
   type: IRNodeTypes.SET_TEXT
   element: number
   values: SimpleExpressionNode[]
+  generated?: boolean // whether this is a generated empty text node by `processTextLikeContainer`
 }
 
 export type KeyOverride = [find: string, replacement: string]
@@ -157,8 +160,7 @@ export interface SetTemplateRefIRNode extends BaseIRNode {
 export interface CreateTextNodeIRNode extends BaseIRNode {
   type: IRNodeTypes.CREATE_TEXT_NODE
   id: number
-  values: SimpleExpressionNode[]
-  effect: boolean
+  values?: SimpleExpressionNode[]
 }
 
 export interface InsertNodeIRNode extends BaseIRNode {
@@ -209,6 +211,11 @@ export interface SlotOutletIRNode extends BaseIRNode {
   fallback?: BlockIRNode
 }
 
+export interface GetTextChildIRNode extends BaseIRNode {
+  type: IRNodeTypes.GET_TEXT_CHILD
+  parent: number
+}
+
 export type IRNode = OperationNode | RootIRNode
 export type OperationNode =
   | SetPropIRNode
@@ -227,6 +234,7 @@ export type OperationNode =
   | CreateComponentIRNode
   | DeclareOldRefIRNode
   | SlotOutletIRNode
+  | GetTextChildIRNode
 
 export enum DynamicFlag {
   NONE = 0,
index 1cace2e47b41bc8612c113025f3d44ed48159879..bf619b17dc29cc826b19aff0852fabd43948ffa7 100644 (file)
@@ -1,7 +1,6 @@
 import {
   type AllNode,
   type TransformOptions as BaseTransformOptions,
-  BindingTypes,
   type CommentNode,
   type CompilerCompatOptions,
   type ElementNode,
@@ -12,7 +11,6 @@ import {
   type TemplateChildNode,
   defaultOnError,
   defaultOnWarn,
-  isConstantNode,
   isVSlot,
 } from '@vue/compiler-dom'
 import { EMPTY_OBJ, NOOP, extend, isArray, isString } from '@vue/shared'
@@ -27,7 +25,7 @@ import {
   type RootIRNode,
   type VaporDirectiveNode,
 } from './ir'
-import { isConstantExpression } from './utils'
+import { isConstantExpression, isStaticExpression } from './utils'
 import { newBlock, newDynamic } from './transforms/utils'
 
 export type NodeTransform = (
@@ -307,21 +305,3 @@ export function createStructuralDirectiveTransform(
     }
   }
 }
-
-function isStaticExpression(
-  context: TransformContext,
-  expressions: SimpleExpressionNode[],
-) {
-  const {
-    options: { bindingMetadata },
-  } = context
-  const isLiteralConst = (name: string) =>
-    bindingMetadata[name] === BindingTypes.LITERAL_CONST
-  return expressions.every(node => {
-    if (node.ast) {
-      return isConstantNode(node.ast, isLiteralConst)
-    } else if (node.ast === null) {
-      return isLiteralConst(node.content)
-    }
-  })
-}
index 37da24453d7283b46b9e65c2c32e1b7b4cff9b78..852cfd463ed4d791ef67e4cb334ff9e3e9039288 100644 (file)
@@ -11,7 +11,11 @@ import {
 } from '@vue/compiler-dom'
 import type { NodeTransform, TransformContext } from '../transform'
 import { DynamicFlag, IRNodeTypes } from '../ir'
-import { getLiteralExpressionValue, isConstantExpression } from '../utils'
+import {
+  getLiteralExpressionValue,
+  isConstantExpression,
+  isStaticExpression,
+} from '../utils'
 
 type TextLike = TextNode | InterpolationNode
 const seen = new WeakMap<
@@ -52,12 +56,24 @@ function processTextLike(context: TransformContext<InterpolationNode>) {
 
   context.dynamic.flags |= DynamicFlag.INSERT | DynamicFlag.NON_TEMPLATE
 
+  const nonConstantExps = values.filter(v => !isConstantExpression(v))
+  const isStatic =
+    !nonConstantExps.length ||
+    isStaticExpression(context, nonConstantExps) ||
+    context.inVOnce
+
   context.registerOperation({
     type: IRNodeTypes.CREATE_TEXT_NODE,
     id,
-    values,
-    effect: !values.every(isConstantExpression) && !context.inVOnce,
+    values: isStatic ? values : undefined,
   })
+  if (!isStatic) {
+    context.registerEffect(values, {
+      type: IRNodeTypes.SET_TEXT,
+      element: id,
+      values,
+    })
+  }
 }
 
 function processTextLikeContainer(
@@ -69,10 +85,17 @@ function processTextLikeContainer(
   if (literals.every(l => l != null)) {
     context.childrenTemplate = literals.map(l => String(l))
   } else {
+    context.childrenTemplate = [' ']
+    context.registerOperation({
+      type: IRNodeTypes.GET_TEXT_CHILD,
+      parent: context.reference(),
+    })
     context.registerEffect(values, {
       type: IRNodeTypes.SET_TEXT,
       element: context.reference(),
       values,
+      // indicates this node is generated, so prefix should be "x" instead of "n"
+      generated: true,
     })
   }
 }
index cb894b0cd9da8cabfa68457d54fe0b3d032b846f..0832398e12a4870eb1fdbcab68ac9b723b6833f4 100644 (file)
@@ -3,6 +3,7 @@ import { IRNodeTypes } from '../ir'
 import { EMPTY_EXPRESSION } from './utils'
 import type { DirectiveTransform } from '../transform'
 import { getLiteralExpressionValue } from '../utils'
+import { isVoidTag } from '../../../shared/src'
 
 export const transformVText: DirectiveTransform = (dir, node, context) => {
   let { exp, loc } = dir
@@ -19,14 +20,25 @@ export const transformVText: DirectiveTransform = (dir, node, context) => {
     context.childrenTemplate.length = 0
   }
 
+  // v-text on void tags do nothing
+  if (isVoidTag(context.node.tag)) {
+    return
+  }
+
   const literal = getLiteralExpressionValue(exp)
   if (literal != null) {
     context.childrenTemplate = [String(literal)]
   } else {
+    context.childrenTemplate = [' ']
+    context.registerOperation({
+      type: IRNodeTypes.GET_TEXT_CHILD,
+      parent: context.reference(),
+    })
     context.registerEffect([exp], {
       type: IRNodeTypes.SET_TEXT,
       element: context.reference(),
       values: [exp],
+      generated: true,
     })
   }
 }
index 319eb9f492f24eea5a26e517ae77000110d31018..b20bd3ef16165dc22109cda7a031a594d75bb320 100644 (file)
@@ -2,16 +2,19 @@ import type { BigIntLiteral, NumericLiteral, StringLiteral } from '@babel/types'
 import { isGloballyAllowed } from '@vue/shared'
 import {
   type AttributeNode,
+  BindingTypes,
   type ElementNode,
   NodeTypes,
   type SimpleExpressionNode,
   findDir as _findDir,
   findProp as _findProp,
   createSimpleExpression,
+  isConstantNode,
   isLiteralWhitelisted,
 } from '@vue/compiler-dom'
 import type { VaporDirectiveNode } from './ir'
 import { EMPTY_EXPRESSION } from './transforms/utils'
+import type { TransformContext } from './transform'
 
 export const findProp = _findProp as (
   node: ElementNode,
@@ -45,6 +48,24 @@ export function isConstantExpression(exp: SimpleExpressionNode): boolean {
   )
 }
 
+export function isStaticExpression(
+  context: TransformContext,
+  expressions: SimpleExpressionNode[],
+): boolean {
+  const {
+    options: { bindingMetadata },
+  } = context
+  const isLiteralConst = (name: string) =>
+    bindingMetadata[name] === BindingTypes.LITERAL_CONST
+  return expressions.every(node => {
+    if (node.ast) {
+      return isConstantNode(node.ast, isLiteralConst)
+    } else if (node.ast === null) {
+      return isLiteralConst(node.content)
+    }
+  })
+}
+
 export function resolveExpression(
   exp: SimpleExpressionNode,
 ): SimpleExpressionNode {
index a8bbd96a99a3a15110307f4cabcd48981fe76f96..4bd3528f447ea86c514891355fea09e58f3f2532 100644 (file)
@@ -28,7 +28,7 @@ describe('api: createVaporApp', () => {
         count: { default: 0 },
       },
       setup(props) {
-        return createTextNode(() => [props.count])
+        return createTextNode(String(props.count))
       },
     })
 
@@ -60,7 +60,7 @@ describe('api: createVaporApp', () => {
         count: { default: 0 },
       },
       setup(props) {
-        return createTextNode(() => [props.count])
+        return createTextNode(String(props.count))
       },
     })
 
@@ -93,7 +93,7 @@ describe('api: createVaporApp', () => {
         try {
           inject('__proto__')
         } catch (e: any) {}
-        return createTextNode([`${foo},${bar}`])
+        return createTextNode(`${foo},${bar}`)
       },
     })
 
@@ -142,12 +142,12 @@ describe('api: createVaporApp', () => {
       },
     }).create()
 
-    const FooBar = () => createTextNode(['foobar!'])
+    const FooBar = () => createTextNode('foobar!')
     app.component('FooBar', FooBar)
     expect(app.component('FooBar')).toBe(FooBar)
 
-    app.component('BarBaz', () => createTextNode(['barbaz!']))
-    app.component('BarBaz', () => createTextNode(['barbaz!']))
+    app.component('BarBaz', () => createTextNode('barbaz!'))
+    app.component('BarBaz', () => createTextNode('barbaz!'))
     expect(
       'Component "BarBaz" has already been registered in target app.',
     ).toHaveBeenWarnedTimes(1)
@@ -309,7 +309,7 @@ describe('api: createVaporApp', () => {
         writable: false,
       })
 
-      app.component('div', () => createTextNode(['div']))
+      app.component('div', () => createTextNode('div'))
       mount()
       expect(
         `Do not use built-in or reserved HTML elements as component id: div`,
index 9a2998a2bc6e4d6c83595823bffd1fb11f103437..ea185e14acc01defa98c0b2bf1de4f2d6da3eb58 100644 (file)
@@ -8,15 +8,16 @@ import {
   reactive,
   readonly,
   ref,
+  toDisplayString,
 } from '@vue/runtime-dom'
 import {
   createComponent,
   createTextNode,
   createVaporApp,
   renderEffect,
-  setText,
 } from '../src'
 import { makeRender } from './_utils'
+import { setElementText } from '../src/dom/prop'
 
 const define = makeRender<any>()
 
@@ -41,7 +42,7 @@ describe('api: provide/inject', () => {
         const foo = inject('foo')
         return (() => {
           const n0 = createTextNode()
-          setText(n0, foo)
+          setElementText(n0, foo)
           return n0
         })()
       },
@@ -71,7 +72,7 @@ describe('api: provide/inject', () => {
         const foo = inject(key)
         return (() => {
           const n0 = createTextNode()
-          setText(n0, foo)
+          setElementText(n0, foo)
           return n0
         })()
       },
@@ -101,7 +102,7 @@ describe('api: provide/inject', () => {
         const bar = inject('bar', 'bar')
         return (() => {
           const n0 = createTextNode()
-          setText(n0, foo + bar)
+          setElementText(n0, foo + bar)
           return n0
         })()
       },
@@ -139,7 +140,7 @@ describe('api: provide/inject', () => {
         const baz = inject('baz')
         return (() => {
           const n0 = createTextNode()
-          setText(n0, [foo, bar, baz].join(','))
+          setElementText(n0, [foo, bar, baz].join(','))
           return n0
         })()
       },
@@ -169,7 +170,7 @@ describe('api: provide/inject', () => {
         return (() => {
           const n0 = createTextNode()
           renderEffect(() => {
-            setText(n0, count.value)
+            setElementText(n0, count.value)
           })
           return n0
         })()
@@ -206,7 +207,7 @@ describe('api: provide/inject', () => {
         return (() => {
           const n0 = createTextNode()
           renderEffect(() => {
-            setText(n0, count.value)
+            setElementText(n0, count.value)
           })
           return n0
         })()
@@ -245,7 +246,7 @@ describe('api: provide/inject', () => {
         return (() => {
           const n0 = createTextNode()
           renderEffect(() => {
-            setText(n0, state.count)
+            setElementText(n0, state.count)
           })
           return n0
         })()
@@ -282,7 +283,7 @@ describe('api: provide/inject', () => {
         return (() => {
           const n0 = createTextNode()
           renderEffect(() => {
-            setText(n0, state.count)
+            setElementText(n0, state.count)
           })
           return n0
         })()
@@ -318,7 +319,7 @@ describe('api: provide/inject', () => {
         expect(foo).toBeUndefined()
         return (() => {
           const n0 = createTextNode()
-          setText(n0, foo)
+          setElementText(n0, foo)
           return n0
         })()
       },
@@ -345,7 +346,7 @@ describe('api: provide/inject', () => {
         const foo = inject('foo', undefined)
         return (() => {
           const n0 = createTextNode()
-          setText(n0, foo)
+          setElementText(n0, foo)
           return n0
         })()
       },
@@ -361,7 +362,7 @@ describe('api: provide/inject', () => {
       setup() {
         provide('foo', 'foo')
         const injection = inject('foo', null)
-        return createTextNode(() => [injection])
+        return createTextNode(toDisplayString(injection))
       },
     }).render()
     expect(host.innerHTML).toBe('')
index d3bbeb8e7a058339449cb27b949a578ebd619f8e..2ad7d2e8ff34be52125165c9dc00ce0ad13da34b 100644 (file)
@@ -26,11 +26,11 @@ import {
   createTextNode,
   insert,
   renderEffect,
-  setText,
   template,
 } from '../src'
 import { makeRender } from './_utils'
 import { ITERATE_KEY } from '@vue/reactivity'
+import { setElementText } from '../src/dom/prop'
 
 const define = makeRender<any>()
 
@@ -73,7 +73,7 @@ describe('api: lifecycle hooks', () => {
         onBeforeUpdate(fn)
         const n0 = createTextNode()
         renderEffect(() => {
-          setText(n0, count.value)
+          setElementText(n0, count.value)
         })
         return n0
       },
@@ -99,7 +99,7 @@ describe('api: lifecycle hooks', () => {
         const n0 = createTextNode()
         renderEffect(() => {
           renderSpy()
-          setText(n0, count.value)
+          setElementText(n0, count.value)
         })
         return n0
       },
@@ -120,7 +120,7 @@ describe('api: lifecycle hooks', () => {
 
         const n0 = createTextNode()
         renderEffect(() => {
-          setText(n0, count.value)
+          setElementText(n0, count.value)
         })
         return n0
       },
@@ -282,7 +282,7 @@ describe('api: lifecycle hooks', () => {
 
         const t0 = template('<div></div>')
         const n0 = t0()
-        renderEffect(() => setText(n0, props.count))
+        renderEffect(() => setElementText(n0, props.count))
         return n0
       },
     }
@@ -334,7 +334,7 @@ describe('api: lifecycle hooks', () => {
 
         const n0 = createTextNode()
         renderEffect(() => {
-          setText(n0, [obj.foo, 'bar' in obj, Object.keys(obj).join('')])
+          setElementText(n0, [obj.foo, 'bar' in obj, Object.keys(obj).join('')])
         })
         return n0
       },
@@ -377,7 +377,7 @@ describe('api: lifecycle hooks', () => {
 
         const n0 = createTextNode()
         renderEffect(() => {
-          setText(n0, [obj.foo, 'bar' in obj, Object.keys(obj).join('')])
+          setElementText(n0, [obj.foo, 'bar' in obj, Object.keys(obj).join('')])
         })
         return n0
       },
@@ -451,7 +451,7 @@ describe('api: lifecycle hooks', () => {
         onUpdated(() => handleUpdated())
 
         const n0 = createTextNode()
-        renderEffect(() => setText(n0, count.value))
+        renderEffect(() => setElementText(n0, count.value))
         const n1 = createComponent(Child, { count: () => count.value })
         return [n0, n1]
       },
@@ -464,7 +464,7 @@ describe('api: lifecycle hooks', () => {
 
         const props = currentInstance!.props
         const n2 = createTextNode()
-        renderEffect(() => setText(n2, props.count))
+        renderEffect(() => setElementText(n2, props.count))
         return n2
       },
     }
@@ -496,7 +496,7 @@ describe('api: lifecycle hooks', () => {
         onUpdated(() => handleUpdated())
 
         const n0 = createTextNode()
-        renderEffect(() => setText(n0, count.value))
+        renderEffect(() => setElementText(n0, count.value))
         const n1 = createComponent(Child, { count: () => count.value })
         return [n0, n1]
       },
@@ -511,7 +511,7 @@ describe('api: lifecycle hooks', () => {
         update = () => count.value++
 
         const n2 = createTextNode()
-        renderEffect(() => setText(n2, count.value))
+        renderEffect(() => setElementText(n2, count.value))
         return n2
       },
     }
index e07ab6c3768a09e9ce91fd10c83090da41dafc0a..5e0d5d98aa0c1870bcacfdb04e31e5a048d3ddc2 100644 (file)
@@ -8,6 +8,7 @@ import {
   insert,
   renderEffect,
   setDynamicProps,
+  setText,
   template,
 } from '../src'
 import { nextTick, reactive, ref, watchEffect } from '@vue/runtime-dom'
@@ -26,7 +27,7 @@ describe('api: setup context', () => {
         }
       },
       render(ctx) {
-        return createTextNode([`${ctx.ref} ${ctx.object.msg} ${ctx.value}`])
+        return createTextNode(`${ctx.ref} ${ctx.object.msg} ${ctx.value}`)
       },
     }).render()
     expect(html()).toMatch(`foo bar baz`)
@@ -35,7 +36,7 @@ describe('api: setup context', () => {
   it('should support returning render function', () => {
     const { html } = define({
       setup() {
-        return createTextNode([`hello`])
+        return createTextNode(`hello`)
       },
     }).render()
     expect(html()).toMatch(`hello`)
@@ -51,7 +52,11 @@ describe('api: setup context', () => {
         watchEffect(() => {
           dummy = props.count
         })
-        return createTextNode(() => [props.count])
+        const n = createTextNode()
+        renderEffect(() => {
+          setText(n, props.count)
+        })
+        return n
       },
     })
 
@@ -148,11 +153,15 @@ describe('api: setup context', () => {
           $: [
             () => ({
               name: 'foo',
-              fn: () => createTextNode(() => [id.value]),
+              fn: () => {
+                const n = createTextNode()
+                renderEffect(() => setText(n, id.value))
+                return n
+              },
             }),
             () => ({
               name: 'bar',
-              fn: () => createTextNode(['bar']),
+              fn: () => createTextNode('bar'),
             }),
           ],
         })
@@ -181,10 +190,9 @@ describe('api: setup context', () => {
         delegate(n0, 'click', () => {
           emit('inc', props.count + 1)
         })
-        insert(
-          createTextNode(() => [props.count]),
-          n0,
-        )
+        const n = createTextNode()
+        renderEffect(() => setText(n, props.count))
+        insert(n, n0)
         return n0
       },
     })
index 3a945bc0962bcc09ea49c1a32be8914222d24982..5fdff8eafe4453b437af4b07e561191639c1d045 100644 (file)
@@ -13,11 +13,11 @@ import {
   createIf,
   createTextNode,
   renderEffect,
-  setText,
   template,
 } from '../src'
 import { makeRender } from './_utils'
 import type { VaporComponentInstance } from '../src/component'
+import { setElementText, setText } from '../src/dom/prop'
 
 const define = makeRender()
 
@@ -100,7 +100,7 @@ describe('component', () => {
         const n = inject<Ref<number>>('foo')!
         n.value++
         const n0 = template('<div></div>')()
-        renderEffect(() => setText(n0, n.value))
+        renderEffect(() => setElementText(n0, n.value))
         return n0
       },
     })
@@ -110,7 +110,7 @@ describe('component', () => {
         const n = ref(0)
         provide('foo', n)
         const n0 = template('<div></div>')()
-        renderEffect(() => setText(n0, n.value))
+        renderEffect(() => setElementText(n0, n.value))
         return [n0, createComponent(Child)]
       },
     }).render()
@@ -129,7 +129,7 @@ describe('component', () => {
           val => emit('update', val),
         )
         const n0 = template('<div></div>')()
-        renderEffect(() => setText(n0, props.value))
+        renderEffect(() => setElementText(n0, props.value))
         return n0
       },
     })
@@ -139,7 +139,7 @@ describe('component', () => {
       setup() {
         const inner = ref(0)
         const n0 = template('<div></div>')()
-        renderEffect(() => setText(n0, inner.value))
+        renderEffect(() => setElementText(n0, inner.value))
         const n1 = createComponent(Child, {
           value: () => outer.value,
           onUpdate: () => (val: number) => (inner.value = val),
@@ -162,7 +162,11 @@ describe('component', () => {
       props: ['count'],
       setup(props: any) {
         onUpdated(() => calls.push('update child'))
-        return createTextNode(() => [`${props.count} - ${a.value}`])
+        const n = createTextNode()
+        renderEffect(() => {
+          setText(n, `${props.count} - ${a.value}`)
+        })
+        return n
       },
     })
 
@@ -206,10 +210,11 @@ describe('component', () => {
       props: ['count'],
       setup(props: any) {
         onUpdated(() => calls.push('update parent'))
-        const n1 = createTextNode(() => [
-          `${globalCount.value} - ${props.count}`,
-        ])
+        const n1 = createTextNode()
         const n2 = createComponent(Child, { count: () => parentCount.value })
+        renderEffect(() => {
+          setText(n1, `${globalCount.value} - ${props.count}`)
+        })
         return [n1, n2]
       },
     })
@@ -240,7 +245,7 @@ describe('component', () => {
         const n1 = template('<h1></h1>')()
         renderEffect(() => {
           spy()
-          setText(n1, props.text)
+          setElementText(n1, props.text)
         })
         return n1
       },
@@ -267,7 +272,7 @@ describe('component', () => {
       const t0 = template('<div></div>')
       const n0 = t0()
       watchEffect(() => {
-        setText(n0, count.value)
+        setElementText(n0, count.value)
       })
       renderEffect(() => {})
       return n0
index 4305a270960704d3600580142ff2c6c305cd84e1..e4076855cb40759392b0aa294f2ca17d336788b3 100644 (file)
@@ -7,11 +7,11 @@ import {
   setDynamicProps,
   setProp,
   setStyle,
-  setText,
   template,
 } from '../src'
 import { makeRender } from './_utils'
 import { stringifyStyle } from '@vue/shared'
+import { setElementText } from '../src/dom/prop'
 
 const define = makeRender<any>()
 
@@ -24,7 +24,7 @@ describe('attribute fallthrough', () => {
       props: ['foo'],
       setup(props: any) {
         const n0 = t0() as Element
-        renderEffect(() => setText(n0, props.foo))
+        renderEffect(() => setElementText(n0, props.foo))
         return n0
       },
     })
@@ -62,7 +62,7 @@ describe('attribute fallthrough', () => {
       inheritAttrs: false,
       setup(props: any) {
         const n0 = t0() as Element
-        renderEffect(() => setText(n0, props.foo))
+        renderEffect(() => setElementText(n0, props.foo))
         return n0
       },
     })
@@ -99,7 +99,7 @@ describe('attribute fallthrough', () => {
       props: ['custom-attr'],
       setup(_: any, { attrs }: any) {
         const n0 = t0() as Element
-        renderEffect(() => setText(n0, attrs.foo))
+        renderEffect(() => setElementText(n0, attrs.foo))
         return n0
       },
     })
index 3f8270b3d90669899a25d90423f862a2cb1f388d..2fd0e9df1a0b8acfdf60f53fb3ed96f751834c1d 100644 (file)
@@ -13,10 +13,10 @@ import {
   createComponent,
   defineVaporComponent,
   renderEffect,
-  setText,
   template,
 } from '../src'
 import { makeRender } from './_utils'
+import { setElementText } from '../src/dom/prop'
 
 const define = makeRender<any>()
 
@@ -243,7 +243,7 @@ describe('component: props', () => {
       props: ['foo'],
       setup(props: any) {
         const n0 = t0()
-        renderEffect(() => setText(n0, props.foo))
+        renderEffect(() => setElementText(n0, props.foo))
         return n0
       },
     })
@@ -396,7 +396,7 @@ describe('component: props', () => {
       },
       setup(props: any) {
         const n0 = t0()
-        renderEffect(() => setText(n0, props.foo))
+        renderEffect(() => setElementText(n0, props.foo))
         return n0
       },
     }).render({
@@ -428,7 +428,7 @@ describe('component: props', () => {
         const t0 = template('<h1></h1>')
         const n0 = t0()
         renderEffect(() => {
-          setText(n0, props.foo.val, props.bar)
+          setElementText(n0, String(props.foo.val) + String(props.bar))
         })
         return n0
       },
@@ -503,7 +503,7 @@ describe('component: props', () => {
         const t0 = template('<div></div>')
         const n0 = t0()
         renderEffect(() =>
-          setText(n0, JSON.stringify(attrs) + Object.keys(attrs)),
+          setElementText(n0, JSON.stringify(attrs) + Object.keys(attrs)),
         )
         return n0
       },
index 149da8b94e5c7ee2c3f932266c0b25d707769442..58076fff9eea213c8180d87ef5ed089951560798 100644 (file)
@@ -10,12 +10,12 @@ import {
   insert,
   prepend,
   renderEffect,
-  setText,
   template,
 } from '../src'
 import { currentInstance, nextTick, ref } from '@vue/runtime-dom'
 import { makeRender } from './_utils'
 import type { DynamicSlot } from '../src/componentSlots'
+import { setElementText } from '../src/dom/prop'
 
 const define = makeRender<any>()
 
@@ -186,7 +186,7 @@ describe('component: slots', () => {
           header: props => {
             const el = template('<h1></h1>')()
             renderEffect(() => {
-              setText(el, props.title)
+              setElementText(el, props.title)
             })
             return el
           },
@@ -273,7 +273,7 @@ describe('component: slots', () => {
               fn: (props: any) => {
                 const el = template('<h1></h1>')()
                 renderEffect(() => {
-                  setText(el, props.title)
+                  setElementText(el, props.title)
                 })
                 return el
               },
index ead9e75cd504befc37d9888ea7bd2bd97f9d5179..e879b7103e5d0da1a90774646017185768663c2d 100644 (file)
@@ -4,6 +4,7 @@ import {
   setAttr,
   setClass,
   setDynamicProps,
+  setElementText,
   setHtml,
   setProp,
   setText,
@@ -11,7 +12,11 @@ import {
 } from '../../src/dom/prop'
 import { setStyle } from '../../src/dom/prop'
 import { VaporComponentInstance } from '../../src/component'
-import { currentInstance, simpleSetCurrentInstance } from '@vue/runtime-dom'
+import {
+  currentInstance,
+  ref,
+  simpleSetCurrentInstance,
+} from '@vue/runtime-dom'
 
 let removeComponentInstance = NOOP
 beforeEach(() => {
@@ -227,7 +232,7 @@ describe('patchProp', () => {
       expect((el as any)._value).toBe(obj)
 
       const option = document.createElement('option')
-      setText(option, 'foo')
+      setElementText(option, 'foo')
       expect(option.value).toBe('foo')
       expect(option.getAttribute('value')).toBe(null)
 
@@ -412,9 +417,9 @@ describe('patchProp', () => {
   })
 
   describe('setText', () => {
-    test('should set textContent', () => {
-      const el = document.createElement('div')
-      setText(el, null)
+    test('should set nodeValue', () => {
+      const el = document.createTextNode('foo')
+      setText(el, '')
       expect(el.textContent).toBe('')
       setText(el, 'foo')
       expect(el.textContent).toBe('foo')
@@ -423,6 +428,18 @@ describe('patchProp', () => {
     })
   })
 
+  describe('setElementText', () => {
+    test('should set textContent w/ toDisplayString', () => {
+      const el = document.createElement('div')
+      setElementText(el, null)
+      expect(el.textContent).toBe('')
+      setElementText(el, { a: 1 })
+      expect(el.textContent).toBe(JSON.stringify({ a: 1 }, null, 2))
+      setElementText(el, ref('bar'))
+      expect(el.textContent).toBe('bar')
+    })
+  })
+
   describe('setHtml', () => {
     test('should set innerHTML', () => {
       const el = document.createElement('div')
index 96a533551c1bdb05ee7c15b4a2afce2449c80717..e696fadd469cda2971f0e9d025202f6ece648bab 100644 (file)
@@ -7,7 +7,6 @@ import {
   createTemplateRefSetter,
   insert,
   renderEffect,
-  setText,
   template,
 } from '../../src'
 import { makeRender } from '../_utils'
@@ -20,6 +19,7 @@ import {
   useTemplateRef,
   watchEffect,
 } from '@vue/runtime-dom'
+import { setElementText } from '../../src/dom/prop'
 
 const define = makeRender()
 
@@ -292,7 +292,7 @@ describe('api: template ref', () => {
         const n0 = t0()
         createTemplateRefSetter()(n0 as Element, el)
         renderEffect(() => {
-          setText(n0, el.value && el.value.getAttribute('id'))
+          setElementText(n0, el.value && el.value.getAttribute('id'))
         })
         return n0
       },
@@ -431,7 +431,7 @@ describe('api: template ref', () => {
                   true,
                 )
                 renderEffect(() => {
-                  setText(n1, item)
+                  setElementText(n1, item)
                 })
                 return n1
               },
@@ -493,7 +493,7 @@ describe('api: template ref', () => {
                   true,
                 )
                 renderEffect(() => {
-                  setText(n1, item)
+                  setElementText(n1, item)
                 })
                 return n1
               },
@@ -553,14 +553,14 @@ describe('api: template ref', () => {
               true,
             )
             renderEffect(() => {
-              setText(n4, item)
+              setElementText(n4, item)
             })
             return n4
           },
         )
         insert(n3, n2 as unknown as ParentNode)
         renderEffect(() => {
-          setText(n1!, String(listRefs.value))
+          setElementText(n1!, String(listRefs.value))
         })
         return n0
       },
index a7e6266a7e145599098e7645061dc753f76457af..e319a884c7639759f3c8996936c11d9efc27e0b7 100644 (file)
@@ -3,7 +3,6 @@ import {
   createIf,
   insert,
   renderEffect,
-  setText,
   template,
   // @ts-expect-error
   withDirectives,
@@ -12,6 +11,7 @@ import { nextTick, ref } from '@vue/runtime-dom'
 import type { Mock } from 'vitest'
 import { makeRender } from './_utils'
 import { unmountComponent } from '../src/component'
+import { setElementText } from '../src/dom/prop'
 
 const define = makeRender()
 
@@ -44,7 +44,7 @@ describe('createIf', () => {
           (spyIfFn ||= vi.fn(() => {
             const n2 = t1()
             renderEffect(() => {
-              setText(n2, count.value)
+              setElementText(n2, count.value)
             })
             return n2
           })),
index f80b9ea6769bf1daafbd338b764c4cbaeff7bec7..76ceba5f2b220bbc3fff060839103aa146734527 100644 (file)
@@ -1,17 +1,6 @@
-import { isArray } from '@vue/shared'
-import { renderEffect } from '../renderEffect'
-import { setText } from './prop'
-
-export function createTextNode(values?: any[] | (() => any[])): Text {
-  const node = document.createTextNode('')
-  if (values) {
-    if (isArray(values)) {
-      setText(node, ...values)
-    } else {
-      renderEffect(() => setText(node, ...values()))
-    }
-  }
-  return node
+/*! #__NO_SIDE_EFFECTS__ */
+export function createTextNode(value = ''): Text {
+  return document.createTextNode(value)
 }
 
 /*! #__NO_SIDE_EFFECTS__ */
index 137f2d807f0ea8983d68df86a0a479e4ac199a93..f464a2f6299e56e87593aea0ba867dddd9dd2a49 100644 (file)
@@ -171,13 +171,26 @@ export function setValue(el: TargetElement, value: any): void {
   }
 }
 
-export function setText(el: Node & { $txt?: string }, ...values: any[]): void {
-  const value =
-    values.length > 1
-      ? values.map(toDisplayString).join('')
-      : toDisplayString(values[0])
+/**
+ * Only called on text nodes!
+ * Compiler should also ensure value passed here is already converted by
+ * `toDisplayString`
+ */
+export function setText(el: Text & { $txt?: string }, value: string): void {
   if (el.$txt !== value) {
-    el.textContent = el.$txt = value
+    el.nodeValue = el.$txt = value
+  }
+}
+
+/**
+ * Used by setDynamicProps only, so need to guard with `toDisplayString`
+ */
+export function setElementText(
+  el: Node & { $txt?: string },
+  value: unknown,
+): void {
+  if (el.$txt !== (value = toDisplayString(value))) {
+    el.textContent = el.$txt = value as string
   }
 }
 
@@ -232,7 +245,7 @@ export function setDynamicProp(
     if (key === 'innerHTML') {
       setHtml(el, value)
     } else if (key === 'textContent') {
-      setText(el, value)
+      setElementText(el, value)
     } else if (key === 'value' && canSetValueDirectly(el.tagName)) {
       setValue(el, value)
     } else {