This commit is contained in:
zhiyuanzmj 2025-06-19 12:11:25 +00:00 committed by GitHub
commit 1087cb3951
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 96 additions and 12 deletions

View File

@ -32,3 +32,25 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
return n0 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("<div> </div>", 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
}"
`;

View File

@ -21,7 +21,14 @@ export function render(_ctx) {
const _setTemplateRef = _createTemplateRefSetter() const _setTemplateRef = _createTemplateRefSetter()
const n0 = t0() const n0 = t0()
let r0 let r0
_renderEffect(() => r0 = _setTemplateRef(n0, bar => _ctx.foo = bar, r0)) _renderEffect(() => {
const _foo = _ctx.foo
r0 = _setTemplateRef(n0, bar => {
_foo.value = bar
;({ baz: _ctx.baz } = bar)
console.log(_foo.value, _ctx.baz)
}, r0)
})
return n0 return n0
}" }"
`; `;

View File

@ -1,9 +1,15 @@
import { BindingTypes } from '@vue/compiler-dom' import { BindingTypes } from '@vue/compiler-dom'
import { transformChildren, transformText } from '../../src' import {
transformChildren,
transformElement,
transformText,
transformVBind,
} from '../../src'
import { makeCompile } from './_utils' import { makeCompile } from './_utils'
const compileWithExpression = makeCompile({ const compileWithExpression = makeCompile({
nodeTransforms: [transformChildren, transformText], nodeTransforms: [transformElement, transformChildren, transformText],
directiveTransforms: { bind: transformVBind },
}) })
describe('compiler: expression', () => { describe('compiler: expression', () => {
@ -31,4 +37,14 @@ describe('compiler: expression', () => {
expect(code).toMatchSnapshot() expect(code).toMatchSnapshot()
expect(code).contains(`$props['bar']`) expect(code).contains(`$props['bar']`)
}) })
test('update expression', () => {
const { code } = compileWithExpression(`
<div :id="String(foo.id++)" :foo="foo" :bar="bar++">
{{ String(foo.id++) }} {{ foo }} {{ bar }}
</div>
`)
expect(code).toMatchSnapshot()
expect(code).contains(`_String(_foo.id++)`)
})
}) })

View File

@ -83,7 +83,11 @@ describe('compiler: template ref transform', () => {
test('function ref', () => { test('function ref', () => {
const { ir, code } = compileWithTransformRef( const { ir, code } = compileWithTransformRef(
`<div :ref="bar => foo = bar" />`, `<div :ref="bar => {
foo.value = bar
;({ baz } = bar)
console.log(foo.value, baz)
}" />`,
) )
expect(ir.block.dynamic.children[0]).toMatchObject({ expect(ir.block.dynamic.children[0]).toMatchObject({
id: 0, id: 0,
@ -103,7 +107,6 @@ describe('compiler: template ref transform', () => {
type: IRNodeTypes.SET_TEMPLATE_REF, type: IRNodeTypes.SET_TEMPLATE_REF,
element: 0, element: 0,
value: { value: {
content: 'bar => foo = bar',
isStatic: false, isStatic: false,
}, },
}, },
@ -112,7 +115,11 @@ describe('compiler: template ref transform', () => {
]) ])
expect(code).toMatchSnapshot() expect(code).toMatchSnapshot()
expect(code).contains('const _setTemplateRef = _createTemplateRefSetter()') expect(code).contains('const _setTemplateRef = _createTemplateRefSetter()')
expect(code).contains('_setTemplateRef(n0, bar => _ctx.foo = bar, r0)') expect(code).contains(`_setTemplateRef(n0, bar => {
_foo.value = bar
;({ baz: _ctx.baz } = bar)
console.log(_foo.value, _ctx.baz)
}, r0)`)
}) })
test('ref + v-if', () => { test('ref + v-if', () => {

View File

@ -244,8 +244,13 @@ export function processExpressions(
expressions: SimpleExpressionNode[], expressions: SimpleExpressionNode[],
): DeclarationResult { ): DeclarationResult {
// analyze variables // analyze variables
const { seenVariable, variableToExpMap, expToVariableMap, seenIdentifier } = const {
analyzeExpressions(expressions) seenVariable,
variableToExpMap,
expToVariableMap,
seenIdentifier,
updatedVariable,
} = analyzeExpressions(expressions)
// process repeated identifiers and member expressions // process repeated identifiers and member expressions
// e.g., `foo[baz]` will be transformed into `foo_baz` // e.g., `foo[baz]` will be transformed into `foo_baz`
@ -255,6 +260,7 @@ export function processExpressions(
variableToExpMap, variableToExpMap,
expToVariableMap, expToVariableMap,
seenIdentifier, seenIdentifier,
updatedVariable,
) )
// process duplicate expressions after identifier and member expression handling. // process duplicate expressions after identifier and member expression handling.
@ -263,6 +269,8 @@ export function processExpressions(
context, context,
expressions, expressions,
varDeclarations, varDeclarations,
updatedVariable,
expToVariableMap,
) )
return genDeclarations([...varDeclarations, ...expDeclarations], context) return genDeclarations([...varDeclarations, ...expDeclarations], context)
@ -273,11 +281,13 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) {
const variableToExpMap = new Map<string, Set<SimpleExpressionNode>>() const variableToExpMap = new Map<string, Set<SimpleExpressionNode>>()
const expToVariableMap = new Map<SimpleExpressionNode, string[]>() const expToVariableMap = new Map<SimpleExpressionNode, string[]>()
const seenIdentifier = new Set<string>() const seenIdentifier = new Set<string>()
const updatedVariable = new Set<string>()
const registerVariable = ( const registerVariable = (
name: string, name: string,
exp: SimpleExpressionNode, exp: SimpleExpressionNode,
isIdentifier: boolean, isIdentifier: boolean,
parentStack: Node[] = [],
) => { ) => {
if (isIdentifier) seenIdentifier.add(name) if (isIdentifier) seenIdentifier.add(name)
seenVariable[name] = (seenVariable[name] || 0) + 1 seenVariable[name] = (seenVariable[name] || 0) + 1
@ -286,6 +296,13 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) {
(variableToExpMap.get(name) || new Set()).add(exp), (variableToExpMap.get(name) || new Set()).add(exp),
) )
expToVariableMap.set(exp, (expToVariableMap.get(exp) || []).concat(name)) expToVariableMap.set(exp, (expToVariableMap.get(exp) || []).concat(name))
if (
parentStack.some(
p => p.type === 'UpdateExpression' || p.type === 'AssignmentExpression',
)
) {
updatedVariable.add(name)
}
} }
for (const exp of expressions) { for (const exp of expressions) {
@ -299,14 +316,20 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) {
const memberExp = extractMemberExpression(parent, name => { const memberExp = extractMemberExpression(parent, name => {
registerVariable(name, exp, true) registerVariable(name, exp, true)
}) })
registerVariable(memberExp, exp, false) registerVariable(memberExp, exp, false, parentStack)
} else if (!parentStack.some(isMemberExpression)) { } else if (!parentStack.some(isMemberExpression)) {
registerVariable(currentNode.name, exp, true) registerVariable(currentNode.name, exp, true, parentStack)
} }
}) })
} }
return { seenVariable, seenIdentifier, variableToExpMap, expToVariableMap } return {
seenVariable,
seenIdentifier,
variableToExpMap,
expToVariableMap,
updatedVariable,
}
} }
function processRepeatedVariables( function processRepeatedVariables(
@ -315,9 +338,11 @@ function processRepeatedVariables(
variableToExpMap: Map<string, Set<SimpleExpressionNode>>, variableToExpMap: Map<string, Set<SimpleExpressionNode>>,
expToVariableMap: Map<SimpleExpressionNode, string[]>, expToVariableMap: Map<SimpleExpressionNode, string[]>,
seenIdentifier: Set<string>, seenIdentifier: Set<string>,
updatedVariable: Set<string>,
): DeclarationValue[] { ): DeclarationValue[] {
const declarations: DeclarationValue[] = [] const declarations: DeclarationValue[] = []
for (const [name, exps] of variableToExpMap) { for (const [name, exps] of variableToExpMap) {
if (updatedVariable.has(name)) continue
if (seenVariable[name] > 1 && exps.size > 0) { if (seenVariable[name] > 1 && exps.size > 0) {
const isIdentifier = seenIdentifier.has(name) const isIdentifier = seenIdentifier.has(name)
const varName = isIdentifier ? name : genVarName(name) const varName = isIdentifier ? name : genVarName(name)
@ -409,12 +434,19 @@ function processRepeatedExpressions(
context: CodegenContext, context: CodegenContext,
expressions: SimpleExpressionNode[], expressions: SimpleExpressionNode[],
varDeclarations: DeclarationValue[], varDeclarations: DeclarationValue[],
updatedVariable: Set<string>,
expToVariableMap: Map<SimpleExpressionNode, string[]>,
): DeclarationValue[] { ): DeclarationValue[] {
const declarations: DeclarationValue[] = [] const declarations: DeclarationValue[] = []
const seenExp = expressions.reduce( const seenExp = expressions.reduce(
(acc, exp) => { (acc, exp) => {
const variables = expToVariableMap.get(exp)
// only handle expressions that are not identifiers // 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 acc[exp.content] = (acc[exp.content] || 0) + 1
} }
return acc return acc