Skip to content

Commit

Permalink
fix(compiler-core/v-on): bail caching for member expression handlers …
Browse files Browse the repository at this point in the history
…on components

to preserve correct arity when it is passed down.
fix #1541
  • Loading branch information
yyx990803 committed Jul 8, 2020
1 parent 00f6031 commit 87c2a1e
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 3 deletions.
11 changes: 10 additions & 1 deletion packages/compiler-core/__tests__/transforms/vOn.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -405,6 +405,15 @@ describe('compiler: transform v-on', () => {
})
})

test('bail on component member expression handler', () => {
const { root } = parseWithVOn(`<comp v-on:click="foo" />`, {
prefixIdentifiers: true,
cacheHandlers: true,
isNativeTag: tag => tag === 'div'
})
expect(root.cached).toBe(0)
})

test('inline function expression handler', () => {
const { root, node } = parseWithVOn(`<div v-on:click="() => foo()" />`, {
prefixIdentifiers: true,
Expand Down
14 changes: 12 additions & 2 deletions packages/compiler-core/src/transforms/vOn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
ExpressionNode,
NodeTypes,
createCompoundExpression,
SimpleExpressionNode
SimpleExpressionNode,
ElementTypes
} from '../ast'
import { capitalize, camelize } from '@vue/shared'
import { createCompilerError, ErrorCodes } from '../errors'
Expand Down Expand Up @@ -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. <transition> 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
Expand Down

0 comments on commit 87c2a1e

Please sign in to comment.