From 87c2a1e50f5317a0c47051b06f419e60e5644a1a Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 8 Jul 2020 11:48:02 -0400 Subject: [PATCH] fix(compiler-core/v-on): bail caching for member expression handlers on components to preserve correct arity when it is passed down. fix #1541 --- .../compiler-core/__tests__/transforms/vOn.spec.ts | 11 ++++++++++- packages/compiler-core/src/transforms/vOn.ts | 14 ++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/compiler-core/__tests__/transforms/vOn.spec.ts b/packages/compiler-core/__tests__/transforms/vOn.spec.ts index 996d63e4..191f4cb5 100644 --- a/packages/compiler-core/__tests__/transforms/vOn.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vOn.spec.ts @@ -13,7 +13,7 @@ import { transformElement } from '../../src/transforms/transformElement' import { transformExpression } from '../../src/transforms/transformExpression' function parseWithVOn(template: string, options: CompilerOptions = {}) { - const ast = parse(template) + const ast = parse(template, options) transform(ast, { nodeTransforms: [transformExpression, transformElement], directiveTransforms: { @@ -405,6 +405,15 @@ describe('compiler: transform v-on', () => { }) }) + test('bail on component member expression handler', () => { + const { root } = parseWithVOn(``, { + prefixIdentifiers: true, + cacheHandlers: true, + isNativeTag: tag => tag === 'div' + }) + expect(root.cached).toBe(0) + }) + test('inline function expression handler', () => { const { root, node } = parseWithVOn(`
`, { prefixIdentifiers: true, diff --git a/packages/compiler-core/src/transforms/vOn.ts b/packages/compiler-core/src/transforms/vOn.ts index 40e61a3f..cffe9b66 100644 --- a/packages/compiler-core/src/transforms/vOn.ts +++ b/packages/compiler-core/src/transforms/vOn.ts @@ -6,7 +6,8 @@ import { ExpressionNode, NodeTypes, createCompoundExpression, - SimpleExpressionNode + SimpleExpressionNode, + ElementTypes } from '../ast' import { capitalize, camelize } from '@vue/shared' import { createCompilerError, ErrorCodes } from '../errors' @@ -76,7 +77,16 @@ export const transformOn: DirectiveTransform = ( // with scope analysis, the function is hoistable if it has no reference // to scope variables. isCacheable = - context.cacheHandlers && !hasScopeRef(exp, context.identifiers) + context.cacheHandlers && + // #1541 bail if this is a member exp handler passed to a component - + // we need to use the original function to preserve arity, + // e.g. relies on checking cb.length to determine + // transition end handling. Inline function is ok since its arity + // is preserved even when cached. + !(isMemberExp && node.tagType === ElementTypes.COMPONENT) && + // bail if the function references closure variables (v-for, v-slot) + // it must be passed fresh to avoid stale values. + !hasScopeRef(exp, context.identifiers) // If the expression is optimizable and is a member expression pointing // to a function, turn it into invocation (and wrap in an arrow function // below) so that it always accesses the latest value when called - thus