Skip to content

Commit 87c2a1e

Browse files
committed
fix(compiler-core/v-on): bail caching for member expression handlers on components
to preserve correct arity when it is passed down. fix #1541
1 parent 00f6031 commit 87c2a1e

File tree

2 files changed

+22
-3
lines changed

2 files changed

+22
-3
lines changed

packages/compiler-core/__tests__/transforms/vOn.spec.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { transformElement } from '../../src/transforms/transformElement'
1313
import { transformExpression } from '../../src/transforms/transformExpression'
1414

1515
function parseWithVOn(template: string, options: CompilerOptions = {}) {
16-
const ast = parse(template)
16+
const ast = parse(template, options)
1717
transform(ast, {
1818
nodeTransforms: [transformExpression, transformElement],
1919
directiveTransforms: {
@@ -405,6 +405,15 @@ describe('compiler: transform v-on', () => {
405405
})
406406
})
407407

408+
test('bail on component member expression handler', () => {
409+
const { root } = parseWithVOn(`<comp v-on:click="foo" />`, {
410+
prefixIdentifiers: true,
411+
cacheHandlers: true,
412+
isNativeTag: tag => tag === 'div'
413+
})
414+
expect(root.cached).toBe(0)
415+
})
416+
408417
test('inline function expression handler', () => {
409418
const { root, node } = parseWithVOn(`<div v-on:click="() => foo()" />`, {
410419
prefixIdentifiers: true,

packages/compiler-core/src/transforms/vOn.ts

+12-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import {
66
ExpressionNode,
77
NodeTypes,
88
createCompoundExpression,
9-
SimpleExpressionNode
9+
SimpleExpressionNode,
10+
ElementTypes
1011
} from '../ast'
1112
import { capitalize, camelize } from '@vue/shared'
1213
import { createCompilerError, ErrorCodes } from '../errors'
@@ -76,7 +77,16 @@ export const transformOn: DirectiveTransform = (
7677
// with scope analysis, the function is hoistable if it has no reference
7778
// to scope variables.
7879
isCacheable =
79-
context.cacheHandlers && !hasScopeRef(exp, context.identifiers)
80+
context.cacheHandlers &&
81+
// #1541 bail if this is a member exp handler passed to a component -
82+
// we need to use the original function to preserve arity,
83+
// e.g. <transition> relies on checking cb.length to determine
84+
// transition end handling. Inline function is ok since its arity
85+
// is preserved even when cached.
86+
!(isMemberExp && node.tagType === ElementTypes.COMPONENT) &&
87+
// bail if the function references closure variables (v-for, v-slot)
88+
// it must be passed fresh to avoid stale values.
89+
!hasScopeRef(exp, context.identifiers)
8090
// If the expression is optimizable and is a member expression pointing
8191
// to a function, turn it into invocation (and wrap in an arrow function
8292
// below) so that it always accesses the latest value when called - thus

0 commit comments

Comments
 (0)