From cba34453db0698a4398cd8be55023b8ec7cbb24c Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 18 Oct 2019 16:20:45 -0400 Subject: [PATCH] refactor(v-on): avoid empty modifier guard with only key modifier --- packages/compiler-core/src/ast.ts | 1 - .../__tests__/transforms/vOn.spec.ts | 13 +++++++ packages/compiler-dom/src/runtimeHelpers.ts | 4 +- packages/compiler-dom/src/transforms/vOn.ts | 37 +++++++++++-------- .../__tests__/directives/vOn.spec.ts | 14 +++---- packages/runtime-dom/src/directives/vOn.ts | 6 +-- packages/runtime-dom/src/index.ts | 2 +- 7 files changed, 47 insertions(+), 30 deletions(-) diff --git a/packages/compiler-core/src/ast.ts b/packages/compiler-core/src/ast.ts index b2e127c0..9825b916 100644 --- a/packages/compiler-core/src/ast.ts +++ b/packages/compiler-core/src/ast.ts @@ -190,7 +190,6 @@ export interface InterpolationNode extends Node { content: ExpressionNode } -// always dynamic export interface CompoundExpressionNode extends Node { type: NodeTypes.COMPOUND_EXPRESSION children: ( diff --git a/packages/compiler-dom/__tests__/transforms/vOn.spec.ts b/packages/compiler-dom/__tests__/transforms/vOn.spec.ts index ac8bec78..a61f0c1e 100644 --- a/packages/compiler-dom/__tests__/transforms/vOn.spec.ts +++ b/packages/compiler-dom/__tests__/transforms/vOn.spec.ts @@ -103,4 +103,17 @@ describe('compiler-dom: transform v-on', () => { } }) }) + + it('should not wrap normal guard if there is only keys guard', () => { + const [prop] = parseVOnProperties(`
`, { + prefixIdentifiers: true + }) + expect(prop).toMatchObject({ + type: NodeTypes.JS_PROPERTY, + value: { + callee: V_ON_KEYS_GUARD, + arguments: [{ content: '_ctx.test' }, '["enter"]'] + } + }) + }) }) diff --git a/packages/compiler-dom/src/runtimeHelpers.ts b/packages/compiler-dom/src/runtimeHelpers.ts index c1405498..382403c0 100644 --- a/packages/compiler-dom/src/runtimeHelpers.ts +++ b/packages/compiler-dom/src/runtimeHelpers.ts @@ -15,6 +15,6 @@ registerRuntimeHelpers({ [V_MODEL_TEXT]: `vModelText`, [V_MODEL_SELECT]: `vModelSelect`, [V_MODEL_DYNAMIC]: `vModelDynamic`, - [V_ON_MODIFIERS_GUARD]: `vOnModifiersGuard`, - [V_ON_KEYS_GUARD]: `vOnKeysGuard` + [V_ON_MODIFIERS_GUARD]: `withModifiers`, + [V_ON_KEYS_GUARD]: `withKeys` }) diff --git a/packages/compiler-dom/src/transforms/vOn.ts b/packages/compiler-dom/src/transforms/vOn.ts index b817611d..7df8fdbb 100644 --- a/packages/compiler-dom/src/transforms/vOn.ts +++ b/packages/compiler-dom/src/transforms/vOn.ts @@ -5,9 +5,7 @@ import { createCallExpression, createObjectExpression, createSimpleExpression, - NodeTypes, - CallExpression, - ObjectExpression + NodeTypes } from '@vue/compiler-core' import { V_ON_MODIFIERS_GUARD, V_ON_KEYS_GUARD } from '../runtimeHelpers' import { makeMap } from '@vue/shared' @@ -31,12 +29,22 @@ export const transformOn: DirectiveTransform = (dir, node, context) => { const baseResult = baseTransform(dir, node, context) if (!modifiers.length) return baseResult - const { key, value } = baseResult.props[0] + let { key, value: handlerExp } = baseResult.props[0] + + // modifiers for addEventListener() options, e.g. .passive & .capture + const eventOptionModifiers = modifiers.filter(isEventOptionModifier) + // modifiers that needs runtime guards const runtimeModifiers = modifiers.filter(m => !isEventOptionModifier(m)) - let handler = createCallExpression(context.helper(V_ON_MODIFIERS_GUARD), [ - value, - JSON.stringify(runtimeModifiers.filter(isNonKeyModifier)) - ]) + + // built-in modifiers that are not keys + const nonKeyModifiers = runtimeModifiers.filter(isNonKeyModifier) + if (nonKeyModifiers.length) { + handlerExp = createCallExpression(context.helper(V_ON_MODIFIERS_GUARD), [ + handlerExp, + JSON.stringify(nonKeyModifiers) + ]) + } + const keyModifiers = runtimeModifiers.filter(m => !isNonKeyModifier(m)) if ( keyModifiers.length && @@ -45,18 +53,15 @@ export const transformOn: DirectiveTransform = (dir, node, context) => { !key.isStatic || isKeyboardEvent(key.content)) ) { - handler = createCallExpression(context.helper(V_ON_KEYS_GUARD), [ - handler, + handlerExp = createCallExpression(context.helper(V_ON_KEYS_GUARD), [ + handlerExp, JSON.stringify(keyModifiers) ]) } - let returnExp: CallExpression | ObjectExpression = handler - - const eventOptionModifiers = modifiers.filter(isEventOptionModifier) if (eventOptionModifiers.length) { - returnExp = createObjectExpression([ - createObjectProperty('handler', handler), + handlerExp = createObjectExpression([ + createObjectProperty('handler', handlerExp), createObjectProperty( 'options', createObjectExpression( @@ -74,7 +79,7 @@ export const transformOn: DirectiveTransform = (dir, node, context) => { } return { - props: [createObjectProperty(key, returnExp)], + props: [createObjectProperty(key, handlerExp)], needRuntime: false } } diff --git a/packages/runtime-dom/__tests__/directives/vOn.spec.ts b/packages/runtime-dom/__tests__/directives/vOn.spec.ts index 21e539fc..fda9d6c0 100644 --- a/packages/runtime-dom/__tests__/directives/vOn.spec.ts +++ b/packages/runtime-dom/__tests__/directives/vOn.spec.ts @@ -1,5 +1,5 @@ import { patchEvent } from '../../src/modules/events' -import { vOnModifiersGuard, vOnKeysGuard } from '@vue/runtime-dom' +import { withModifiers, withKeys } from '@vue/runtime-dom' function triggerEvent( target: Element, @@ -21,7 +21,7 @@ describe('runtime-dom: v-on directive', () => { const parent = document.createElement('div') const child = document.createElement('input') parent.appendChild(child) - const childNextValue = vOnModifiersGuard(jest.fn(), ['prevent', 'stop']) + const childNextValue = withModifiers(jest.fn(), ['prevent', 'stop']) patchEvent(child, 'click', null, childNextValue, null) const parentNextValue = jest.fn() patchEvent(parent, 'click', null, parentNextValue, null) @@ -34,7 +34,7 @@ describe('runtime-dom: v-on directive', () => { const child = document.createElement('input') parent.appendChild(child) const fn = jest.fn() - const handler = vOnModifiersGuard(fn, ['self']) + const handler = withModifiers(fn, ['self']) patchEvent(parent, 'click', null, handler, null) triggerEvent(child, 'click') expect(fn).not.toBeCalled() @@ -44,7 +44,7 @@ describe('runtime-dom: v-on directive', () => { const el = document.createElement('div') const fn = jest.fn() //
- const nextValue = vOnKeysGuard(vOnModifiersGuard(fn, ['ctrl']), ['esc']) + const nextValue = withKeys(withModifiers(fn, ['ctrl']), ['esc']) patchEvent(el, 'keyup', null, nextValue, null) triggerEvent(el, 'keyup', e => (e.key = 'a')) expect(fn).not.toBeCalled() @@ -64,7 +64,7 @@ describe('runtime-dom: v-on directive', () => { const el = document.createElement('div') // Case 1:
const fn1 = jest.fn() - const next1 = vOnModifiersGuard(fn1, ['exact']) + const next1 = withModifiers(fn1, ['exact']) patchEvent(el, 'keyup', null, next1, null) triggerEvent(el, 'keyup') expect(fn1.mock.calls.length).toBe(1) @@ -72,7 +72,7 @@ describe('runtime-dom: v-on directive', () => { expect(fn1.mock.calls.length).toBe(1) // Case 2:
const fn2 = jest.fn() - const next2 = vOnKeysGuard(vOnModifiersGuard(fn2, ['ctrl', 'exact']), ['a']) + const next2 = withKeys(withModifiers(fn2, ['ctrl', 'exact']), ['a']) patchEvent(el, 'keyup', null, next2, null) triggerEvent(el, 'keyup', e => (e.key = 'a')) expect(fn2).not.toBeCalled() @@ -96,7 +96,7 @@ describe('runtime-dom: v-on directive', () => { buttons.forEach(button => { const el = document.createElement('div') const fn = jest.fn() - const handler = vOnModifiersGuard(fn, [button]) + const handler = withModifiers(fn, [button]) patchEvent(el, 'mousedown', null, handler, null) buttons.filter(b => b !== button).forEach(button => { triggerEvent(el, 'mousedown', e => (e.button = buttonCodes[button])) diff --git a/packages/runtime-dom/src/directives/vOn.ts b/packages/runtime-dom/src/directives/vOn.ts index 5ffbcc30..b8b91d48 100644 --- a/packages/runtime-dom/src/directives/vOn.ts +++ b/packages/runtime-dom/src/directives/vOn.ts @@ -1,6 +1,6 @@ const systemModifiers = ['ctrl', 'shift', 'alt', 'meta'] -type KeyedEvent = KeyboardEvent | MouseEvent | TouchEvent; +type KeyedEvent = KeyboardEvent | MouseEvent | TouchEvent const modifierGuards: Record< string, @@ -20,7 +20,7 @@ const modifierGuards: Record< systemModifiers.some(m => (e as any)[`${m}Key`] && !modifiers.includes(m)) } -export const vOnModifiersGuard = (fn: Function, modifiers: string[]) => { +export const withModifiers = (fn: Function, modifiers: string[]) => { return (event: Event) => { for (let i = 0; i < modifiers.length; i++) { const guard = modifierGuards[modifiers[i]] @@ -42,7 +42,7 @@ const keyNames: Record = { delete: 'backspace' } -export const vOnKeysGuard = (fn: Function, modifiers: string[]) => { +export const withKeys = (fn: Function, modifiers: string[]) => { return (event: KeyboardEvent) => { if (!('key' in event)) return const eventKey = event.key.toLowerCase() diff --git a/packages/runtime-dom/src/index.ts b/packages/runtime-dom/src/index.ts index 35167655..a7b9e44b 100644 --- a/packages/runtime-dom/src/index.ts +++ b/packages/runtime-dom/src/index.ts @@ -31,7 +31,7 @@ export { vModelDynamic } from './directives/vModel' -export { vOnModifiersGuard, vOnKeysGuard } from './directives/vOn' +export { withModifiers, withKeys } from './directives/vOn' // re-export everything from core // h, Component, reactivity API, nextTick, flags & types