]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-core/v-on): bail caching for member expression handlers on components
authorEvan You <yyx990803@gmail.com>
Wed, 8 Jul 2020 15:48:02 +0000 (11:48 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 8 Jul 2020 15:48:12 +0000 (11:48 -0400)
to preserve correct arity when it is passed down.
fix #1541

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

index 996d63e4909539de36c0e9c14d2c12a7c00a2a0f..191f4cb50189da79b024399fdc0a35a65f03ce86 100644 (file)
@@ -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(`<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,
index 40e61a3f7b3dd36cca7166caaa2d49073a2b1d89..cffe9b664496c35dd66c3249be9579b57627e14b 100644 (file)
@@ -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. <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