fix(compiler-vapor): cache setDynamicProps deps when possible

This commit is contained in:
daiwei 2024-12-11 18:05:41 +08:00
parent 1bd7136775
commit 21e241c85e
9 changed files with 69 additions and 42 deletions

View 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
"
`;

View 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))',
)
})

View 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
}"
`;

View 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
}"
`;

View 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
}"
`;

View 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))',
)
})

View 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)))',
)
})

View 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(', '))
}

View 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
}