}"
`;
+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)
}"
`;
+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)
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" />`,
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')
+ })
})
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(
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)
+ }
})
}
return ''
}
}
+
+const isMemberExpression = (node: Node) => {
+ return (
+ node.type === 'MemberExpression' || node.type === 'OptionalMemberExpression'
+ )
+}