fix(compiler-core/v-on): bail caching for member expression handlers on components
to preserve correct arity when it is passed down. fix #1541
This commit is contained in:
parent
00f6031fbf
commit
87c2a1e50f
@ -13,7 +13,7 @@ import { transformElement } from '../../src/transforms/transformElement'
|
|||||||
import { transformExpression } from '../../src/transforms/transformExpression'
|
import { transformExpression } from '../../src/transforms/transformExpression'
|
||||||
|
|
||||||
function parseWithVOn(template: string, options: CompilerOptions = {}) {
|
function parseWithVOn(template: string, options: CompilerOptions = {}) {
|
||||||
const ast = parse(template)
|
const ast = parse(template, options)
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
nodeTransforms: [transformExpression, transformElement],
|
nodeTransforms: [transformExpression, transformElement],
|
||||||
directiveTransforms: {
|
directiveTransforms: {
|
||||||
@ -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', () => {
|
test('inline function expression handler', () => {
|
||||||
const { root, node } = parseWithVOn(`<div v-on:click="() => foo()" />`, {
|
const { root, node } = parseWithVOn(`<div v-on:click="() => foo()" />`, {
|
||||||
prefixIdentifiers: true,
|
prefixIdentifiers: true,
|
||||||
|
@ -6,7 +6,8 @@ import {
|
|||||||
ExpressionNode,
|
ExpressionNode,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
createCompoundExpression,
|
createCompoundExpression,
|
||||||
SimpleExpressionNode
|
SimpleExpressionNode,
|
||||||
|
ElementTypes
|
||||||
} from '../ast'
|
} from '../ast'
|
||||||
import { capitalize, camelize } from '@vue/shared'
|
import { capitalize, camelize } from '@vue/shared'
|
||||||
import { createCompilerError, ErrorCodes } from '../errors'
|
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
|
// with scope analysis, the function is hoistable if it has no reference
|
||||||
// to scope variables.
|
// to scope variables.
|
||||||
isCacheable =
|
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
|
// If the expression is optimizable and is a member expression pointing
|
||||||
// to a function, turn it into invocation (and wrap in an arrow function
|
// 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
|
// below) so that it always accesses the latest value when called - thus
|
||||||
|
Loading…
x
Reference in New Issue
Block a user