]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-vapor): cache setDynamicProps deps when possible
authordaiwei <daiwei521@126.com>
Wed, 11 Dec 2024 10:05:41 +0000 (18:05 +0800)
committerdaiwei <daiwei521@126.com>
Wed, 11 Dec 2024 10:05:41 +0000 (18:05 +0800)
packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
packages/compiler-vapor/__tests__/compile.spec.ts
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vModel.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts
packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
packages/compiler-vapor/src/generators/operation.ts
packages/compiler-vapor/src/generators/prop.ts

index c4551d506ccfcfd8d434785c026ecfac31d267bd..b865d6189b4e1f57583e2c7c94b093245248537a 100644 (file)
@@ -206,7 +206,7 @@ exports[`compile > expression parsing > v-bind 1`] = `
 "
   const n0 = t0()
   let _key_value, _foo, _key_value_foo
-  _renderEffect(() => (_key_value !== key.value || _foo !== _unref(foo)) && (_key_value_foo = _setDynamicProps(n0, _key_value_foo, [{ [key.value+1]: _unref(foo)[key.value+1]() }], true)))
+  _renderEffect(() => (_key_value !== key.value || _foo !== _unref(foo)) && (_key_value_foo = _setDynamicProps(n0, _key_value_foo, [{ [(_key_value = key.value)+1]: (_foo = _unref(foo))[key.value+1]() }], true)))
   return n0
 "
 `;
index 9b8770d59a6bc3caa890ccda26f75a58f24f8889..422f7a88d85477e9fa5bd7f649ffa91eadd789dc 100644 (file)
@@ -195,7 +195,7 @@ describe('compile', () => {
       expect(code).matchSnapshot()
       expect(code).contains('key.value+1')
       expect(code).contains(
-        '(_key_value !== key.value || _foo !== _unref(foo)) && (_key_value_foo = _setDynamicProps(n0, _key_value_foo, [{ [key.value+1]: _unref(foo)[key.value+1]() }], true))',
+        '(_key_value !== key.value || _foo !== _unref(foo)) && (_key_value_foo = _setDynamicProps(n0, _key_value_foo, [{ [(_key_value = key.value)+1]: (_foo = _unref(foo))[key.value+1]() }], true))',
       )
     })
 
index 874a378776b4409ea49f15f322dc97590cbbfbd2..0f8f5317a556e120b9a5b62901843153be538e1f 100644 (file)
@@ -346,10 +346,10 @@ exports[`compiler: element transform > v-bind="obj" 1`] = `
 "import { setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue';
 const t0 = _template("<div></div>")
 
-export function render(_ctx) {
+export function render(_ctx, $props, $emit, $attrs, $slots) {
   const n0 = t0()
-  let _obj, _prev_obj
-  _renderEffect(() => _obj !== _ctx.obj && (_prev_obj = _setDynamicProps(n0, _prev_obj, [_ctx.obj], true)))
+  let _prev_obj
+  _renderEffect(() => _prev_obj = _setDynamicProps(n0, _prev_obj, [_ctx.obj], true))
   return n0
 }"
 `;
@@ -358,10 +358,10 @@ exports[`compiler: element transform > v-bind="obj" after static prop 1`] = `
 "import { setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue';
 const t0 = _template("<div></div>")
 
-export function render(_ctx) {
+export function render(_ctx, $props, $emit, $attrs, $slots) {
   const n0 = t0()
   let _obj, _prev_obj
-  _renderEffect(() => _obj !== _ctx.obj && (_prev_obj = _setDynamicProps(n0, _prev_obj, [{ id: "foo" }, _ctx.obj], true)))
+  _renderEffect(() => _obj !== _ctx.obj && (_prev_obj = _setDynamicProps(n0, _prev_obj, [{ id: "foo" }, (_obj = _ctx.obj)], true)))
   return n0
 }"
 `;
@@ -370,10 +370,10 @@ exports[`compiler: element transform > v-bind="obj" before static prop 1`] = `
 "import { setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue';
 const t0 = _template("<div></div>")
 
-export function render(_ctx) {
+export function render(_ctx, $props, $emit, $attrs, $slots) {
   const n0 = t0()
-  let _obj, _prev_obj
-  _renderEffect(() => _obj !== _ctx.obj && (_prev_obj = _setDynamicProps(n0, _prev_obj, [_ctx.obj, { id: "foo" }], true)))
+  let _prev_obj
+  _renderEffect(() => _prev_obj = _setDynamicProps(n0, _prev_obj, [_ctx.obj, { id: "foo" }], true))
   return n0
 }"
 `;
@@ -385,7 +385,7 @@ const t0 = _template("<div></div>")
 export function render(_ctx) {
   const n0 = t0()
   let _obj, _prev_obj
-  _renderEffect(() => _obj !== _ctx.obj && (_prev_obj = _setDynamicProps(n0, _prev_obj, [{ id: "foo" }, _ctx.obj, { class: "bar" }], true)))
+  _renderEffect(() => _obj !== _ctx.obj && (_prev_obj = _setDynamicProps(n0, _prev_obj, [{ id: "foo" }, (_obj = _ctx.obj), { class: "bar" }], true)))
   return n0
 }"
 `;
index d19901d5af9c687b98cce159a747046d88acbfd0..036fbcd7e17b09b9f97b6b472266c7c3b39c9186 100644 (file)
@@ -79,7 +79,7 @@ const t0 = _template("<div></div>")
 export function render(_ctx) {
   const n0 = t0()
   let _id, _prev_id
-  _renderEffect(() => _id !== _ctx.id && (_prev_id = _setDynamicProp(n0, "fooBar", _prev_id, _ctx.id)))
+  _renderEffect(() => _id !== _ctx.id && (_prev_id = _setDynamicProp(n0, "fooBar", _prev_id, (_id = _ctx.id))))
   return n0
 }"
 `;
@@ -91,7 +91,7 @@ const t0 = _template("<div></div>")
 export function render(_ctx) {
   const n0 = t0()
   let _foo, _id, _foo_id
-  _renderEffect(() => (_foo !== _ctx.foo || _id !== _ctx.id) && (_foo_id = _setDynamicProps(n0, _foo_id, [{ [_camelize(_ctx.foo)]: _ctx.id }], true)))
+  _renderEffect(() => (_foo !== _ctx.foo || _id !== _ctx.id) && (_foo_id = _setDynamicProps(n0, _foo_id, [{ [_camelize((_foo = _ctx.foo))]: (_id = _ctx.id) }], true)))
   return n0
 }"
 `;
@@ -103,7 +103,7 @@ const t0 = _template("<div></div>")
 export function render(_ctx) {
   const n0 = t0()
   let _fooBar, _prev_fooBar
-  _renderEffect(() => _fooBar !== _ctx.fooBar && (_prev_fooBar = _setDynamicProp(n0, "fooBar", _prev_fooBar, _ctx.fooBar)))
+  _renderEffect(() => _fooBar !== _ctx.fooBar && (_prev_fooBar = _setDynamicProp(n0, "fooBar", _prev_fooBar, (_fooBar = _ctx.fooBar))))
   return n0
 }"
 `;
@@ -199,7 +199,7 @@ const t0 = _template("<div></div>")
 export function render(_ctx) {
   const n0 = t0()
   let _fooBar, _id, _fooBar_id
-  _renderEffect(() => (_fooBar !== _ctx.fooBar || _id !== _ctx.id) && (_fooBar_id = _setDynamicProps(n0, _fooBar_id, [{ ["." + _ctx.fooBar]: _ctx.id }], true)))
+  _renderEffect(() => (_fooBar !== _ctx.fooBar || _id !== _ctx.id) && (_fooBar_id = _setDynamicProps(n0, _fooBar_id, [{ ["." + (_fooBar = _ctx.fooBar)]: (_id = _ctx.id) }], true)))
   return n0
 }"
 `;
@@ -307,7 +307,7 @@ const t0 = _template("<progress></progress>")
 export function render(_ctx) {
   const n0 = t0()
   let _foo, _prev_foo
-  _renderEffect(() => _foo !== _ctx.foo && (_prev_foo = _setDynamicProp(n0, "value", _prev_foo, _ctx.foo)))
+  _renderEffect(() => _foo !== _ctx.foo && (_prev_foo = _setDynamicProp(n0, "value", _prev_foo, (_foo = _ctx.foo))))
   return n0
 }"
 `;
@@ -440,7 +440,7 @@ const t0 = _template("<div></div>")
 export function render(_ctx) {
   const n0 = t0()
   let _id, _title, _id_title
-  _renderEffect(() => (_id !== _ctx.id || _title !== _ctx.title) && (_id_title = _setDynamicProps(n0, _id_title, [{ [_ctx.id]: _ctx.id, [_ctx.title]: _ctx.title }], true)))
+  _renderEffect(() => (_id !== _ctx.id || _title !== _ctx.title) && (_id_title = _setDynamicProps(n0, _id_title, [{ [(_id = _ctx.id)]: _ctx.id, [(_title = _ctx.title)]: _ctx.title }], true)))
   return n0
 }"
 `;
@@ -452,7 +452,7 @@ const t0 = _template("<div></div>")
 export function render(_ctx) {
   const n0 = t0()
   let _id, _prev_id
-  _renderEffect(() => _id !== _ctx.id && (_prev_id = _setDynamicProps(n0, _prev_id, [{ [_ctx.id]: _ctx.id, foo: "bar", checked: "" }], true)))
+  _renderEffect(() => _id !== _ctx.id && (_prev_id = _setDynamicProps(n0, _prev_id, [{ [(_id = _ctx.id)]: _ctx.id, foo: "bar", checked: "" }], true)))
   return n0
 }"
 `;
@@ -464,7 +464,7 @@ const t0 = _template("<div></div>")
 export function render(_ctx) {
   const n0 = t0()
   let _camelCase, _prev_camelCase
-  _renderEffect(() => _camelCase !== _ctx.camelCase && (_prev_camelCase = _setDynamicProp(n0, "camel-case", _prev_camelCase, _ctx.camelCase)))
+  _renderEffect(() => _camelCase !== _ctx.camelCase && (_prev_camelCase = _setDynamicProp(n0, "camel-case", _prev_camelCase, (_camelCase = _ctx.camelCase))))
   return n0
 }"
 `;
index f5e794fcda8f943b826be58d9b9064319a96777f..0ee0799c81fac2aa304474b3c03e0f535dfa0047 100644 (file)
@@ -243,7 +243,7 @@ export function render(_ctx) {
   _withDirectives(n0, [[_vModelDynamic, () => _ctx.model]])
   _delegate(n0, "update:modelValue", () => $event => (_ctx.model = $event))
   let _obj, _prev_obj
-  _renderEffect(() => _obj !== _ctx.obj && (_prev_obj = _setDynamicProps(n0, _prev_obj, [_ctx.obj], true)))
+  _renderEffect(() => _obj !== _ctx.obj && (_prev_obj = _setDynamicProps(n0, _prev_obj, [(_obj = _ctx.obj)], true)))
   return n0
 }"
 `;
index 8fa8f688f42970274624c45ee569ab60dd7af58b..1e76b12fa638c89cdff30286fddb68c86006359a 100644 (file)
@@ -543,7 +543,11 @@ describe('compiler: element transform', () => {
   })
 
   test('v-bind="obj"', () => {
-    const { code, ir } = compileWithElementTransform(`<div v-bind="obj" />`)
+    const { code, ir } = compileWithElementTransform(`<div v-bind="obj" />`, {
+      bindingMetadata: {
+        obj: BindingTypes.SETUP_REACTIVE_CONST,
+      },
+    })
     expect(code).toMatchSnapshot()
     expect(ir.block.effect).toMatchObject([
       {
@@ -573,13 +577,18 @@ describe('compiler: element transform', () => {
       },
     ])
     expect(code).contains(
-      '_obj !== _ctx.obj && (_prev_obj = _setDynamicProps(n0, _prev_obj, [_ctx.obj], true))',
+      '_prev_obj = _setDynamicProps(n0, _prev_obj, [_ctx.obj], true)',
     )
   })
 
   test('v-bind="obj" after static prop', () => {
     const { code, ir } = compileWithElementTransform(
       `<div id="foo" v-bind="obj" />`,
+      {
+        bindingMetadata: {
+          obj: BindingTypes.SETUP_REF,
+        },
+      },
     )
     expect(code).toMatchSnapshot()
     expect(ir.block.effect).toMatchObject([
@@ -611,13 +620,18 @@ describe('compiler: element transform', () => {
       },
     ])
     expect(code).contains(
-      '_obj !== _ctx.obj && (_prev_obj = _setDynamicProps(n0, _prev_obj, [{ id: "foo" }, _ctx.obj], true))',
+      '_obj !== _ctx.obj && (_prev_obj = _setDynamicProps(n0, _prev_obj, [{ id: "foo" }, (_obj = _ctx.obj)], true))',
     )
   })
 
   test('v-bind="obj" before static prop', () => {
     const { code, ir } = compileWithElementTransform(
       `<div v-bind="obj" id="foo" />`,
+      {
+        bindingMetadata: {
+          obj: BindingTypes.SETUP_REACTIVE_CONST,
+        },
+      },
     )
     expect(code).toMatchSnapshot()
     expect(ir.block.effect).toMatchObject([
@@ -639,7 +653,7 @@ describe('compiler: element transform', () => {
       },
     ])
     expect(code).contains(
-      '_obj !== _ctx.obj && (_prev_obj = _setDynamicProps(n0, _prev_obj, [_ctx.obj, { id: "foo" }], true))',
+      '_prev_obj = _setDynamicProps(n0, _prev_obj, [_ctx.obj, { id: "foo" }], true)',
     )
   })
 
@@ -668,7 +682,7 @@ describe('compiler: element transform', () => {
       },
     ])
     expect(code).contains(
-      '_obj !== _ctx.obj && (_prev_obj = _setDynamicProps(n0, _prev_obj, [{ id: "foo" }, _ctx.obj, { class: "bar" }], true))',
+      '_obj !== _ctx.obj && (_prev_obj = _setDynamicProps(n0, _prev_obj, [{ id: "foo" }, (_obj = _ctx.obj), { class: "bar" }], true))',
     )
   })
 
index 12f231e40be99d8d0d15fc34f034d018324f3cb3..6ea7ac133e6d6a296a893254c19e13f4b630bf62 100644 (file)
@@ -131,7 +131,7 @@ describe('compiler v-bind', () => {
       },
     })
     expect(code).contains(
-      '_camelCase !== _ctx.camelCase && (_prev_camelCase = _setDynamicProp(n0, "camel-case", _prev_camelCase, _ctx.camelCase))',
+      '_camelCase !== _ctx.camelCase && (_prev_camelCase = _setDynamicProp(n0, "camel-case", _prev_camelCase, (_camelCase = _ctx.camelCase)))',
     )
   })
 
@@ -177,7 +177,7 @@ describe('compiler v-bind', () => {
       ],
     })
     expect(code).contains(
-      '(_id !== _ctx.id || _title !== _ctx.title) && (_id_title = _setDynamicProps(n0, _id_title, [{ [_ctx.id]: _ctx.id, [_ctx.title]: _ctx.title }], true))',
+      '(_id !== _ctx.id || _title !== _ctx.title) && (_id_title = _setDynamicProps(n0, _id_title, [{ [(_id = _ctx.id)]: _ctx.id, [(_title = _ctx.title)]: _ctx.title }], true))',
     )
   })
 
@@ -230,7 +230,7 @@ describe('compiler v-bind', () => {
       ],
     })
     expect(code).contains(
-      '_id !== _ctx.id && (_prev_id = _setDynamicProps(n0, _prev_id, [{ [_ctx.id]: _ctx.id, foo: "bar", checked: "" }], true))',
+      '_id !== _ctx.id && (_prev_id = _setDynamicProps(n0, _prev_id, [{ [(_id = _ctx.id)]: _ctx.id, foo: "bar", checked: "" }], true))',
     )
   })
 
@@ -293,7 +293,7 @@ describe('compiler v-bind', () => {
 
     expect(code).matchSnapshot()
     expect(code).contains(
-      '_id !== _ctx.id && (_prev_id = _setDynamicProp(n0, "fooBar", _prev_id, _ctx.id))',
+      '_id !== _ctx.id && (_prev_id = _setDynamicProp(n0, "fooBar", _prev_id, (_id = _ctx.id)))',
     )
   })
 
@@ -319,7 +319,7 @@ describe('compiler v-bind', () => {
     })
     expect(code).contains('renderEffect')
     expect(code).contains(
-      '_fooBar !== _ctx.fooBar && (_prev_fooBar = _setDynamicProp(n0, "fooBar", _prev_fooBar, _ctx.fooBar))',
+      '_fooBar !== _ctx.fooBar && (_prev_fooBar = _setDynamicProp(n0, "fooBar", _prev_fooBar, (_fooBar = _ctx.fooBar)))',
     )
   })
 
@@ -351,7 +351,7 @@ describe('compiler v-bind', () => {
     expect(code).matchSnapshot()
     expect(code).contains('renderEffect')
     expect(code).contains(
-      `(_foo !== _ctx.foo || _id !== _ctx.id) && (_foo_id = _setDynamicProps(n0, _foo_id, [{ [_camelize(_ctx.foo)]: _ctx.id }], true))`,
+      `(_foo !== _ctx.foo || _id !== _ctx.id) && (_foo_id = _setDynamicProps(n0, _foo_id, [{ [_camelize((_foo = _ctx.foo))]: (_id = _ctx.id) }], true))`,
     )
   })
 
@@ -436,7 +436,7 @@ describe('compiler v-bind', () => {
     })
     expect(code).contains('renderEffect')
     expect(code).contains(
-      `(_fooBar !== _ctx.fooBar || _id !== _ctx.id) && (_fooBar_id = _setDynamicProps(n0, _fooBar_id, [{ ["." + _ctx.fooBar]: _ctx.id }], true))`,
+      `(_fooBar !== _ctx.fooBar || _id !== _ctx.id) && (_fooBar_id = _setDynamicProps(n0, _fooBar_id, [{ ["." + (_fooBar = _ctx.fooBar)]: (_id = _ctx.id) }], true))`,
     )
   })
 
@@ -788,7 +788,7 @@ describe('compiler v-bind', () => {
     `)
     expect(code).matchSnapshot()
     expect(code).contains(
-      '_foo !== _ctx.foo && (_prev_foo = _setDynamicProp(n0, "value", _prev_foo, _ctx.foo))',
+      '_foo !== _ctx.foo && (_prev_foo = _setDynamicProp(n0, "value", _prev_foo, (_foo = _ctx.foo)))',
     )
   })
 
index 6554b5e77769aeff89b8407db807ca4a49c47411..2435340dba38f41b6ddc77198b19d6fff3b6cd6d 100644 (file)
@@ -112,11 +112,11 @@ export function genEffect(
   context: CodegenContext,
   allDeclareNames: Set<string>,
 ): CodeFragment[] {
-  const { processingRenderEffect } = context
   const [frag, push] = buildCodeFragment()
-  const { declareNames, earlyCheckExps } = processingRenderEffect!
   const operationsExps = genOperations(operations, context)
 
+  const { processingRenderEffect } = context
+  const { declareNames, earlyCheckExps } = processingRenderEffect!
   if (declareNames.size) {
     allDeclareNames.add([...declareNames].join(', '))
   }
index 817cce2aba5d54445b088cd38c8c15f2f729210f..822ef3db9ffca83a605684d497d5d1ee6e78739a 100644 (file)
@@ -1,4 +1,5 @@
 import {
+  BindingTypes,
   NewlineType,
   type SimpleExpressionNode,
   isSimpleIdentifier,
@@ -243,22 +244,28 @@ function processPropValues(
   const { shouldCacheRenderEffectDeps, processingRenderEffect } = context
   // single-line render effect and the operation needs cache return a value,
   // the expression needs to be wrapped in parentheses.
-  // e.g. _foo === _ctx.foo && (_foo = _setStyle(...))
+  // e.g. _foo === _ctx.foo && (_prev_foo = _setStyle(...))
   let shouldWrapInParentheses: boolean = false
   let prevValueName
   if (shouldCacheRenderEffectDeps()) {
+    const { declareNames, operations, identifiers } = processingRenderEffect!
+    // if render effect rely on any reactive object, it should not cache
+    const canCache = identifiers.every(name => canCacheValue(context, name))
     const needReturnValue = helpersNeedCachedReturnValue.includes(helperName)
-    processValues(context, values, !needReturnValue)
-    const { declareNames } = processingRenderEffect!
+    processValues(context, values, canCache)
     // if the operation needs to cache the return value and has multiple declareNames,
     // combine them into a single name as the return value name.
     if (declareNames.size > 0 && needReturnValue) {
       const names = [...declareNames]
       prevValueName =
         declareNames.size === 1 ? `_prev${names[0]}` : names.join('')
+      if (!canCache) {
+        declareNames.clear()
+        processingRenderEffect!.earlyCheckExps.splice(0)
+      }
       declareNames.add(prevValueName)
     }
-    shouldWrapInParentheses = processingRenderEffect!.operations.length === 1
+    shouldWrapInParentheses = operations.length === 1 && canCache
   }
   return { prevValueName, shouldWrapInParentheses }
 }
@@ -271,8 +278,7 @@ export function processValues(
   const allCheckExps: string[] = []
   values.forEach(value => {
     const checkExps = processValue(context, value, needRewrite)
-    if (checkExps && checkExps.length > 0)
-      allCheckExps.push(...checkExps, ' && ')
+    if (checkExps && checkExps.length > 0) allCheckExps.push(...checkExps)
   })
 
   return allCheckExps.length > 0
@@ -319,6 +325,13 @@ function processValue(
   }
 
   if (earlyCheckExps.length > 0) {
-    return [[...new Set(earlyCheckExps)].join(' && ')]
+    return [...new Set(earlyCheckExps)]
   }
 }
+
+function canCacheValue(context: CodegenContext, name: string): boolean {
+  const {
+    options: { bindingMetadata },
+  } = context
+  return bindingMetadata[name] !== BindingTypes.SETUP_REACTIVE_CONST
+}