Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -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("<div></div>", 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("<div></div>", true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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("<div></div>", 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("<div></div>", true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,40 @@ describe('compiler: template ref transform', () => {
expect(code).contains('_setTemplateRef(n0, _ctx.foo, r0)')
})

test('function ref', () => {
const { ir, code } = compileWithTransformRef(
`<div :ref="bar => foo = bar" />`,
)
expect(ir.block.dynamic.children[0]).toMatchObject({
id: 0,
flags: DynamicFlag.REFERENCED,
})
expect(ir.template).toEqual(['<div></div>'])
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(
`<div ref="foo" v-if="true" />`,
Expand Down
8 changes: 8 additions & 0 deletions packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(`
<div v-bind="{ foo: bar => foo = bar }"></div>
`)
expect(code).matchSnapshot()
expect(code).not.contains('const _bar = _ctx.bar')
})
})
43 changes: 15 additions & 28 deletions packages/compiler-vapor/src/generators/expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -295,33 +294,15 @@ 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)
} else if (!parentStack.some(isMemberExpression)) {
registerVariable(currentNode.name, exp, true)
}
})
}

Expand Down Expand Up @@ -580,3 +561,9 @@ function extractMemberExpression(
return ''
}
}

const isMemberExpression = (node: Node) => {
return (
node.type === 'MemberExpression' || node.type === 'OptionalMemberExpression'
)
}