diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap
index 518c2a5fe70..454e50e9cbc 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap
@@ -32,3 +32,25 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
return n0
}"
`;
+
+exports[`compiler: expression > update expression 1`] = `
+"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template("
", true)
+
+export function render(_ctx) {
+ const n1 = t0()
+ const n0 = _child(n1)
+ const x1 = _child(n1)
+ _renderEffect(() => {
+ const _String = String
+ const _foo = _ctx.foo
+
+ _setText(n0, _toDisplayString(_String(_foo.id++)) + " " + _toDisplayString(_foo) + " " + _toDisplayString(_ctx.bar))
+ _setText(x1, _toDisplayString(_String(_foo.id++)) + " " + _toDisplayString(_foo) + " " + _toDisplayString(_ctx.bar))
+ _setProp(n1, "id", _String(_foo.id++))
+ _setProp(n1, "foo", _foo)
+ _setProp(n1, "bar", _ctx.bar++)
+ })
+ return n1
+}"
+`;
diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap
index f2eade4bcdf..4a691056ae2 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap
@@ -13,6 +13,19 @@ export function render(_ctx) {
}"
`;
+exports[`compiler: template ref transform > function ref 1`] = `
+"import { createTemplateRefSetter as _createTemplateRefSetter, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template("", true)
+
+export function render(_ctx) {
+ const _setTemplateRef = _createTemplateRefSetter()
+ const n0 = t0()
+ let r0
+ _renderEffect(() => r0 = _setTemplateRef(n0, bar => _ctx.foo = bar, r0))
+ return n0
+}"
+`;
+
exports[`compiler: template ref transform > ref + v-for 1`] = `
"import { createTemplateRefSetter as _createTemplateRefSetter, createFor as _createFor, template as _template } from 'vue';
const t0 = _template("", true)
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 6e7d4229df8..9ffac2fb932 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap
@@ -75,6 +75,17 @@ export function render(_ctx) {
}"
`;
+exports[`cache multiple access > not cache variable in function expression 1`] = `
+"import { setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template("", true)
+
+export function render(_ctx) {
+ const n0 = t0()
+ _renderEffect(() => _setDynamicProps(n0, [{ foo: bar => _ctx.foo = bar }], true))
+ return n0
+}"
+`;
+
exports[`cache multiple access > not cache variable only used in property shorthand 1`] = `
"import { setStyle as _setStyle, renderEffect as _renderEffect, template as _template } from 'vue';
const t0 = _template("", true)
diff --git a/packages/compiler-vapor/__tests__/transforms/expression.spec.ts b/packages/compiler-vapor/__tests__/transforms/expression.spec.ts
index c97decd9ddd..5983bde67d1 100644
--- a/packages/compiler-vapor/__tests__/transforms/expression.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/expression.spec.ts
@@ -1,9 +1,15 @@
import { BindingTypes } from '@vue/compiler-dom'
-import { transformChildren, transformText } from '../../src'
+import {
+ transformChildren,
+ transformElement,
+ transformText,
+ transformVBind,
+} from '../../src'
import { makeCompile } from './_utils'
const compileWithExpression = makeCompile({
- nodeTransforms: [transformChildren, transformText],
+ nodeTransforms: [transformElement, transformChildren, transformText],
+ directiveTransforms: { bind: transformVBind },
})
describe('compiler: expression', () => {
@@ -31,4 +37,14 @@ describe('compiler: expression', () => {
expect(code).toMatchSnapshot()
expect(code).contains(`$props['bar']`)
})
+
+ test('update expression', () => {
+ const { code } = compileWithExpression(`
+
+ {{ String(foo.id++) }} {{ foo }} {{ bar }}
+
+ `)
+ expect(code).toMatchSnapshot()
+ expect(code).contains(`_String(_foo.id++)`)
+ })
})
diff --git a/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts
index 6be8f18779c..f026675e4eb 100644
--- a/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts
@@ -81,6 +81,40 @@ describe('compiler: template ref transform', () => {
expect(code).contains('_setTemplateRef(n0, _ctx.foo, r0)')
})
+ test('function ref', () => {
+ const { ir, code } = compileWithTransformRef(
+ ``,
+ )
+ expect(ir.block.dynamic.children[0]).toMatchObject({
+ id: 0,
+ flags: DynamicFlag.REFERENCED,
+ })
+ expect(ir.template).toEqual([''])
+ expect(ir.block.operation).toMatchObject([
+ {
+ type: IRNodeTypes.DECLARE_OLD_REF,
+ id: 0,
+ },
+ ])
+ expect(ir.block.effect).toMatchObject([
+ {
+ operations: [
+ {
+ type: IRNodeTypes.SET_TEMPLATE_REF,
+ element: 0,
+ value: {
+ content: 'bar => foo = bar',
+ isStatic: false,
+ },
+ },
+ ],
+ },
+ ])
+ expect(code).toMatchSnapshot()
+ expect(code).contains('const _setTemplateRef = _createTemplateRefSetter()')
+ expect(code).contains('_setTemplateRef(n0, bar => _ctx.foo = bar, r0)')
+ })
+
test('ref + v-if', () => {
const { ir, code } = compileWithTransformRef(
``,
diff --git a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
index 9a5f6ab6971..60c3ebf0c27 100644
--- a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
@@ -809,4 +809,12 @@ describe('cache multiple access', () => {
expect(code).matchSnapshot()
expect(code).not.contains('const _bar = _ctx.bar')
})
+
+ test('not cache variable in function expression', () => {
+ const { code } = compileWithVBind(`
+
+ `)
+ expect(code).matchSnapshot()
+ expect(code).not.contains('const _bar = _ctx.bar')
+ })
})
diff --git a/packages/compiler-vapor/src/generators/expression.ts b/packages/compiler-vapor/src/generators/expression.ts
index eedaeeb380a..1baa8553590 100644
--- a/packages/compiler-vapor/src/generators/expression.ts
+++ b/packages/compiler-vapor/src/generators/expression.ts
@@ -20,7 +20,6 @@ import type { Identifier, Node } from '@babel/types'
import type { CodegenContext } from '../generate'
import { isConstantExpression } from '../utils'
import { type CodeFragment, NEWLINE, buildCodeFragment } from './utils'
-import { walk } from 'estree-walker'
import { type ParserOptions, parseExpression } from '@babel/parser'
export function genExpression(
@@ -245,8 +244,13 @@ export function processExpressions(
expressions: SimpleExpressionNode[],
): DeclarationResult {
// analyze variables
- const { seenVariable, variableToExpMap, expToVariableMap, seenIdentifier } =
- analyzeExpressions(expressions)
+ const {
+ seenVariable,
+ variableToExpMap,
+ expToVariableMap,
+ seenIdentifier,
+ updatedVariable,
+ } = analyzeExpressions(expressions)
// process repeated identifiers and member expressions
// e.g., `foo[baz]` will be transformed into `foo_baz`
@@ -256,6 +260,7 @@ export function processExpressions(
variableToExpMap,
expToVariableMap,
seenIdentifier,
+ updatedVariable,
)
// process duplicate expressions after identifier and member expression handling.
@@ -264,6 +269,8 @@ export function processExpressions(
context,
expressions,
varDeclarations,
+ updatedVariable,
+ expToVariableMap,
)
return genDeclarations([...varDeclarations, ...expDeclarations], context)
@@ -274,11 +281,13 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) {
const variableToExpMap = new Map>()
const expToVariableMap = new Map()
const seenIdentifier = new Set()
+ const updatedVariable = new Set()
const registerVariable = (
name: string,
exp: SimpleExpressionNode,
isIdentifier: boolean,
+ parentStack: Node[] = [],
) => {
if (isIdentifier) seenIdentifier.add(name)
seenVariable[name] = (seenVariable[name] || 0) + 1
@@ -287,6 +296,8 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) {
(variableToExpMap.get(name) || new Set()).add(exp),
)
expToVariableMap.set(exp, (expToVariableMap.get(exp) || []).concat(name))
+ if (parentStack.some(p => p.type === 'UpdateExpression'))
+ updatedVariable.add(name)
}
for (const exp of expressions) {
@@ -295,37 +306,25 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) {
continue
}
- walk(exp.ast, {
- enter(currentNode: Node, parent: Node | null) {
- if (currentNode.type === 'MemberExpression') {
- const memberExp = extractMemberExpression(
- currentNode,
- (name: string) => {
- registerVariable(name, exp, true)
- },
- )
- registerVariable(memberExp, exp, false)
- return this.skip()
- }
-
- // skip shorthand or non-computed property keys
- if (
- parent &&
- parent.type === 'ObjectProperty' &&
- parent.key === currentNode &&
- (parent.shorthand || !parent.computed)
- ) {
- return this.skip()
- }
-
- if (currentNode.type === 'Identifier') {
- registerVariable(currentNode.name, exp, true)
- }
- },
+ walkIdentifiers(exp.ast, (currentNode, parent, parentStack) => {
+ if (parent && isMemberExpression(parent)) {
+ const memberExp = extractMemberExpression(parent, name => {
+ registerVariable(name, exp, true)
+ })
+ registerVariable(memberExp, exp, false, parentStack)
+ } else if (!parentStack.some(isMemberExpression)) {
+ registerVariable(currentNode.name, exp, true, parentStack)
+ }
})
}
- return { seenVariable, seenIdentifier, variableToExpMap, expToVariableMap }
+ return {
+ seenVariable,
+ seenIdentifier,
+ variableToExpMap,
+ expToVariableMap,
+ updatedVariable,
+ }
}
function processRepeatedVariables(
@@ -334,9 +333,11 @@ function processRepeatedVariables(
variableToExpMap: Map>,
expToVariableMap: Map,
seenIdentifier: Set,
+ updatedVariable: Set,
): DeclarationValue[] {
const declarations: DeclarationValue[] = []
for (const [name, exps] of variableToExpMap) {
+ if (updatedVariable.has(name)) continue
if (seenVariable[name] > 1 && exps.size > 0) {
const isIdentifier = seenIdentifier.has(name)
const varName = isIdentifier ? name : genVarName(name)
@@ -428,12 +429,19 @@ function processRepeatedExpressions(
context: CodegenContext,
expressions: SimpleExpressionNode[],
varDeclarations: DeclarationValue[],
+ updatedVariable: Set,
+ expToVariableMap: Map,
): DeclarationValue[] {
const declarations: DeclarationValue[] = []
const seenExp = expressions.reduce(
(acc, exp) => {
+ const variables = expToVariableMap.get(exp)
// only handle expressions that are not identifiers
- if (exp.ast && exp.ast.type !== 'Identifier') {
+ if (
+ exp.ast &&
+ exp.ast.type !== 'Identifier' &&
+ !(variables && variables.some(v => updatedVariable.has(v)))
+ ) {
acc[exp.content] = (acc[exp.content] || 0) + 1
}
return acc
@@ -580,3 +588,9 @@ function extractMemberExpression(
return ''
}
}
+
+const isMemberExpression = (node: Node) => {
+ return (
+ node.type === 'MemberExpression' || node.type === 'OptionalMemberExpression'
+ )
+}