]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-vapor): improve expression caching for shared member roots (#14132)
authoredison <daiwei521@126.com>
Mon, 24 Nov 2025 12:10:45 +0000 (20:10 +0800)
committerGitHub <noreply@github.com>
Mon, 24 Nov 2025 12:10:45 +0000 (20:10 +0800)
packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
packages/compiler-vapor/src/generators/expression.ts

index e19adce62af56b33efc94cbec35a1d60995cffea..e2c94526b9bd959dda5ed98c93323b16120d2a8b 100644 (file)
@@ -8,7 +8,7 @@ export function render(_ctx) {
   const n0 = t0()
   _renderEffect(() => {
     const _obj = _ctx.obj
-    _setProp(n0, "id", _obj.foo + _obj.bar)
+    _setProp(n0, "id", _obj!.foo + _obj!.bar)
   })
   return n0
 }"
@@ -238,6 +238,38 @@ export function render(_ctx) {
 }"
 `;
 
+exports[`cache multiple access > shared member root 1`] = `
+"import { setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template("<div></div>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  const n1 = t0()
+  _renderEffect(() => {
+    const _foo = _ctx.foo
+    _setProp(n0, "id", _foo.bar)
+    _setProp(n1, "id", _foo.baz)
+  })
+  return [n0, n1]
+}"
+`;
+
+exports[`cache multiple access > shared member root with TSNonNullExpression 1`] = `
+"import { setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template("<div></div>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  const n1 = t0()
+  _renderEffect(() => {
+    const _foo = _ctx.foo
+    _setProp(n0, "id", _foo!.bar)
+    _setProp(n1, "id", _foo!.baz)
+  })
+  return [n0, n1]
+}"
+`;
+
 exports[`cache multiple access > variable name substring edge cases 1`] = `
 "import { setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
 const t0 = _template("<div></div>", true)
index 0d60c02fd54e94d994b2c2a53862ade72c310017..230ae2c4da415605e347e1b6bfb66db1f3585958 100644 (file)
@@ -850,7 +850,29 @@ describe('cache multiple access', () => {
     const { code } = compileWithVBind(`<div :id="obj!.foo + obj!.bar"></div>`)
     expect(code).matchSnapshot()
     expect(code).contains('const _obj = _ctx.obj')
-    expect(code).contains('_setProp(n0, "id", _obj.foo + _obj.bar)')
+    expect(code).contains('_setProp(n0, "id", _obj!.foo + _obj!.bar)')
+  })
+
+  test('shared member root', () => {
+    const { code } = compileWithVBind(`
+        <div :id="foo.bar"></div>
+        <div :id="foo.baz"></div>
+      `)
+    expect(code).matchSnapshot()
+    expect(code).contains('const _foo = _ctx.foo')
+    expect(code).contains('_setProp(n0, "id", _foo.bar)')
+    expect(code).contains('_setProp(n1, "id", _foo.baz)')
+  })
+
+  test('shared member root with TSNonNullExpression', () => {
+    const { code } = compileWithVBind(`
+        <div :id="foo!.bar"></div>
+        <div :id="foo!.baz"></div>
+      `)
+    expect(code).matchSnapshot()
+    expect(code).contains('const _foo = _ctx.foo')
+    expect(code).contains('_setProp(n0, "id", _foo!.bar)')
+    expect(code).contains('_setProp(n1, "id", _foo!.baz)')
   })
 
   test('not cache variable only used in property shorthand', () => {
index 75803d5629bb544625008ccdda1e47b2632df065..9012ee1f2e145d4993d53c30c2bf5854fed8f18b 100644 (file)
@@ -492,10 +492,10 @@ function shouldDeclareVariable(
     }
     return true
   }
-  // if arrays share common elements, no declaration needed
-  // because they will be treat as repeated expressions
+  // if arrays are identical, no declaration needed
+  // because they will be treated as repeated expressions
   // e.g., [[foo,bar],[foo,bar]] -> const foo_bar = _ctx.foo + _ctx.bar
-  if (vars.some(v => v.some(e => first.includes(e)))) {
+  if (vars.every(v => v.every((e, idx) => e === first[idx]))) {
     return false
   }
 
@@ -679,7 +679,7 @@ function extractMemberExpression(
         : `.${extractMemberExpression(exp.property, NOOP)}`
       return `${object}${prop}`
     case 'TSNonNullExpression': // foo!.bar
-      return `${extractMemberExpression(exp.expression, onIdentifier)}!`
+      return `${extractMemberExpression(exp.expression, onIdentifier)}`
     default:
       return ''
   }