Skip to content

Commit ed05053

Browse files
authored
fix(compiler-vapor): prevent caching UpdateExpression (#13346)
1 parent a6e4966 commit ed05053

File tree

5 files changed

+96
-12
lines changed

5 files changed

+96
-12
lines changed

packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,25 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
3232
return n0
3333
}"
3434
`;
35+
36+
exports[`compiler: expression > update expression 1`] = `
37+
"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
38+
const t0 = _template("<div> </div>", true)
39+
40+
export function render(_ctx) {
41+
const n1 = t0()
42+
const n0 = _child(n1)
43+
const x1 = _child(n1)
44+
_renderEffect(() => {
45+
const _String = String
46+
const _foo = _ctx.foo
47+
48+
_setText(n0, _toDisplayString(_String(_foo.id++)) + " " + _toDisplayString(_foo) + " " + _toDisplayString(_ctx.bar))
49+
_setText(x1, _toDisplayString(_String(_foo.id++)) + " " + _toDisplayString(_foo) + " " + _toDisplayString(_ctx.bar))
50+
_setProp(n1, "id", _String(_foo.id++))
51+
_setProp(n1, "foo", _foo)
52+
_setProp(n1, "bar", _ctx.bar++)
53+
})
54+
return n1
55+
}"
56+
`;

packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,14 @@ export function render(_ctx) {
2121
const _setTemplateRef = _createTemplateRefSetter()
2222
const n0 = t0()
2323
let r0
24-
_renderEffect(() => r0 = _setTemplateRef(n0, bar => _ctx.foo = bar, r0))
24+
_renderEffect(() => {
25+
const _foo = _ctx.foo
26+
r0 = _setTemplateRef(n0, bar => {
27+
_foo.value = bar
28+
;({ baz: _ctx.baz } = bar)
29+
console.log(_foo.value, _ctx.baz)
30+
}, r0)
31+
})
2532
return n0
2633
}"
2734
`;

packages/compiler-vapor/__tests__/transforms/expression.spec.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
import { BindingTypes } from '@vue/compiler-dom'
2-
import { transformChildren, transformText } from '../../src'
2+
import {
3+
transformChildren,
4+
transformElement,
5+
transformText,
6+
transformVBind,
7+
} from '../../src'
38
import { makeCompile } from './_utils'
49

510
const compileWithExpression = makeCompile({
6-
nodeTransforms: [transformChildren, transformText],
11+
nodeTransforms: [transformElement, transformChildren, transformText],
12+
directiveTransforms: { bind: transformVBind },
713
})
814

915
describe('compiler: expression', () => {
@@ -31,4 +37,14 @@ describe('compiler: expression', () => {
3137
expect(code).toMatchSnapshot()
3238
expect(code).contains(`$props['bar']`)
3339
})
40+
41+
test('update expression', () => {
42+
const { code } = compileWithExpression(`
43+
<div :id="String(foo.id++)" :foo="foo" :bar="bar++">
44+
{{ String(foo.id++) }} {{ foo }} {{ bar }}
45+
</div>
46+
`)
47+
expect(code).toMatchSnapshot()
48+
expect(code).contains(`_String(_foo.id++)`)
49+
})
3450
})

packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,11 @@ describe('compiler: template ref transform', () => {
8383

8484
test('function ref', () => {
8585
const { ir, code } = compileWithTransformRef(
86-
`<div :ref="bar => foo = bar" />`,
86+
`<div :ref="bar => {
87+
foo.value = bar
88+
;({ baz } = bar)
89+
console.log(foo.value, baz)
90+
}" />`,
8791
)
8892
expect(ir.block.dynamic.children[0]).toMatchObject({
8993
id: 0,
@@ -103,7 +107,6 @@ describe('compiler: template ref transform', () => {
103107
type: IRNodeTypes.SET_TEMPLATE_REF,
104108
element: 0,
105109
value: {
106-
content: 'bar => foo = bar',
107110
isStatic: false,
108111
},
109112
},
@@ -112,7 +115,11 @@ describe('compiler: template ref transform', () => {
112115
])
113116
expect(code).toMatchSnapshot()
114117
expect(code).contains('const _setTemplateRef = _createTemplateRefSetter()')
115-
expect(code).contains('_setTemplateRef(n0, bar => _ctx.foo = bar, r0)')
118+
expect(code).contains(`_setTemplateRef(n0, bar => {
119+
_foo.value = bar
120+
;({ baz: _ctx.baz } = bar)
121+
console.log(_foo.value, _ctx.baz)
122+
}, r0)`)
116123
})
117124

118125
test('ref + v-if', () => {

packages/compiler-vapor/src/generators/expression.ts

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,13 @@ export function processExpressions(
244244
expressions: SimpleExpressionNode[],
245245
): DeclarationResult {
246246
// analyze variables
247-
const { seenVariable, variableToExpMap, expToVariableMap, seenIdentifier } =
248-
analyzeExpressions(expressions)
247+
const {
248+
seenVariable,
249+
variableToExpMap,
250+
expToVariableMap,
251+
seenIdentifier,
252+
updatedVariable,
253+
} = analyzeExpressions(expressions)
249254

250255
// process repeated identifiers and member expressions
251256
// e.g., `foo[baz]` will be transformed into `foo_baz`
@@ -255,6 +260,7 @@ export function processExpressions(
255260
variableToExpMap,
256261
expToVariableMap,
257262
seenIdentifier,
263+
updatedVariable,
258264
)
259265

260266
// process duplicate expressions after identifier and member expression handling.
@@ -263,6 +269,8 @@ export function processExpressions(
263269
context,
264270
expressions,
265271
varDeclarations,
272+
updatedVariable,
273+
expToVariableMap,
266274
)
267275

268276
return genDeclarations([...varDeclarations, ...expDeclarations], context)
@@ -273,11 +281,13 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) {
273281
const variableToExpMap = new Map<string, Set<SimpleExpressionNode>>()
274282
const expToVariableMap = new Map<SimpleExpressionNode, string[]>()
275283
const seenIdentifier = new Set<string>()
284+
const updatedVariable = new Set<string>()
276285

277286
const registerVariable = (
278287
name: string,
279288
exp: SimpleExpressionNode,
280289
isIdentifier: boolean,
290+
parentStack: Node[] = [],
281291
) => {
282292
if (isIdentifier) seenIdentifier.add(name)
283293
seenVariable[name] = (seenVariable[name] || 0) + 1
@@ -286,6 +296,13 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) {
286296
(variableToExpMap.get(name) || new Set()).add(exp),
287297
)
288298
expToVariableMap.set(exp, (expToVariableMap.get(exp) || []).concat(name))
299+
if (
300+
parentStack.some(
301+
p => p.type === 'UpdateExpression' || p.type === 'AssignmentExpression',
302+
)
303+
) {
304+
updatedVariable.add(name)
305+
}
289306
}
290307

291308
for (const exp of expressions) {
@@ -299,14 +316,20 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) {
299316
const memberExp = extractMemberExpression(parent, name => {
300317
registerVariable(name, exp, true)
301318
})
302-
registerVariable(memberExp, exp, false)
319+
registerVariable(memberExp, exp, false, parentStack)
303320
} else if (!parentStack.some(isMemberExpression)) {
304-
registerVariable(currentNode.name, exp, true)
321+
registerVariable(currentNode.name, exp, true, parentStack)
305322
}
306323
})
307324
}
308325

309-
return { seenVariable, seenIdentifier, variableToExpMap, expToVariableMap }
326+
return {
327+
seenVariable,
328+
seenIdentifier,
329+
variableToExpMap,
330+
expToVariableMap,
331+
updatedVariable,
332+
}
310333
}
311334

312335
function processRepeatedVariables(
@@ -315,9 +338,11 @@ function processRepeatedVariables(
315338
variableToExpMap: Map<string, Set<SimpleExpressionNode>>,
316339
expToVariableMap: Map<SimpleExpressionNode, string[]>,
317340
seenIdentifier: Set<string>,
341+
updatedVariable: Set<string>,
318342
): DeclarationValue[] {
319343
const declarations: DeclarationValue[] = []
320344
for (const [name, exps] of variableToExpMap) {
345+
if (updatedVariable.has(name)) continue
321346
if (seenVariable[name] > 1 && exps.size > 0) {
322347
const isIdentifier = seenIdentifier.has(name)
323348
const varName = isIdentifier ? name : genVarName(name)
@@ -409,12 +434,19 @@ function processRepeatedExpressions(
409434
context: CodegenContext,
410435
expressions: SimpleExpressionNode[],
411436
varDeclarations: DeclarationValue[],
437+
updatedVariable: Set<string>,
438+
expToVariableMap: Map<SimpleExpressionNode, string[]>,
412439
): DeclarationValue[] {
413440
const declarations: DeclarationValue[] = []
414441
const seenExp = expressions.reduce(
415442
(acc, exp) => {
443+
const variables = expToVariableMap.get(exp)
416444
// only handle expressions that are not identifiers
417-
if (exp.ast && exp.ast.type !== 'Identifier') {
445+
if (
446+
exp.ast &&
447+
exp.ast.type !== 'Identifier' &&
448+
!(variables && variables.some(v => updatedVariable.has(v)))
449+
) {
418450
acc[exp.content] = (acc[exp.content] || 0) + 1
419451
}
420452
return acc

0 commit comments

Comments
 (0)