From 21e241c85e35a6f211116e6d20200e4fba825adf Mon Sep 17 00:00:00 2001 From: daiwei Date: Wed, 11 Dec 2024 18:05:41 +0800 Subject: [PATCH] fix(compiler-vapor): cache setDynamicProps deps when possible --- .../__snapshots__/compile.spec.ts.snap | 2 +- .../compiler-vapor/__tests__/compile.spec.ts | 2 +- .../transformElement.spec.ts.snap | 18 ++++++------- .../__snapshots__/vBind.spec.ts.snap | 16 +++++------ .../__snapshots__/vModel.spec.ts.snap | 2 +- .../transforms/transformElement.spec.ts | 24 +++++++++++++---- .../__tests__/transforms/vBind.spec.ts | 16 +++++------ .../src/generators/operation.ts | 4 +-- .../compiler-vapor/src/generators/prop.ts | 27 ++++++++++++++----- 9 files changed, 69 insertions(+), 42 deletions(-) diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap index c4551d506..b865d6189 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap @@ -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 " `; diff --git a/packages/compiler-vapor/__tests__/compile.spec.ts b/packages/compiler-vapor/__tests__/compile.spec.ts index 9b8770d59..422f7a88d 100644 --- a/packages/compiler-vapor/__tests__/compile.spec.ts +++ b/packages/compiler-vapor/__tests__/compile.spec.ts @@ -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))', ) }) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap index 874a37877..0f8f5317a 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap @@ -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("
") -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("
") -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("
") -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("
") 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 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap index d19901d5a..036fbcd7e 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap @@ -79,7 +79,7 @@ const t0 = _template("
") 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("
") 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("
") 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("
") 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("") 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("
") 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("
") 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("
") 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 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vModel.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vModel.spec.ts.snap index f5e794fcd..0ee0799c8 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vModel.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vModel.spec.ts.snap @@ -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 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts index 8fa8f688f..1e76b12fa 100644 --- a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts @@ -543,7 +543,11 @@ describe('compiler: element transform', () => { }) test('v-bind="obj"', () => { - const { code, ir } = compileWithElementTransform(`
`) + const { code, ir } = compileWithElementTransform(`
`, { + 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( `
`, + { + 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( `
`, + { + 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))', ) }) diff --git a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts index 12f231e40..6ea7ac133 100644 --- a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts @@ -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)))', ) }) diff --git a/packages/compiler-vapor/src/generators/operation.ts b/packages/compiler-vapor/src/generators/operation.ts index 6554b5e77..2435340db 100644 --- a/packages/compiler-vapor/src/generators/operation.ts +++ b/packages/compiler-vapor/src/generators/operation.ts @@ -112,11 +112,11 @@ export function genEffect( context: CodegenContext, allDeclareNames: Set, ): 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(', ')) } diff --git a/packages/compiler-vapor/src/generators/prop.ts b/packages/compiler-vapor/src/generators/prop.ts index 817cce2ab..822ef3db9 100644 --- a/packages/compiler-vapor/src/generators/prop.ts +++ b/packages/compiler-vapor/src/generators/prop.ts @@ -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 +}