fix(v-on): refactor DOM event options modifer handling
fix #1567 Previously multiple `v-on` handlers with different event attach option modifers (`.once`, `.capture` and `.passive`) are generated as an array of objects in the form of `[{ handler, options }]` - however, this makes it pretty complex for `runtime-dom` to properly handle all possible value permutations, as each handler may need to be attached with different options. With this commit, they are now generated as event props with different keys - e.g. `v-on:click.capture` is now generated as a prop named `onClick.capture`. This allows them to be patched as separate props which makes the runtime handling much simpler.
This commit is contained in:
@@ -5,16 +5,15 @@ import {
|
||||
ElementNode,
|
||||
ObjectExpression,
|
||||
NodeTypes,
|
||||
VNodeCall
|
||||
VNodeCall,
|
||||
helperNameMap,
|
||||
CAPITALIZE
|
||||
} from '@vue/compiler-core'
|
||||
import { transformOn } from '../../src/transforms/vOn'
|
||||
import { V_ON_WITH_MODIFIERS, V_ON_WITH_KEYS } from '../../src/runtimeHelpers'
|
||||
import { transformElement } from '../../../compiler-core/src/transforms/transformElement'
|
||||
import { transformExpression } from '../../../compiler-core/src/transforms/transformExpression'
|
||||
import {
|
||||
createObjectMatcher,
|
||||
genFlagText
|
||||
} from '../../../compiler-core/__tests__/testUtils'
|
||||
import { genFlagText } from '../../../compiler-core/__tests__/testUtils'
|
||||
import { PatchFlags } from '@vue/shared'
|
||||
|
||||
function parseWithVOn(template: string, options: CompilerOptions = {}) {
|
||||
@@ -83,42 +82,37 @@ describe('compiler-dom: transform v-on', () => {
|
||||
})
|
||||
expect(prop).toMatchObject({
|
||||
type: NodeTypes.JS_PROPERTY,
|
||||
value: createObjectMatcher({
|
||||
handler: {
|
||||
callee: V_ON_WITH_MODIFIERS,
|
||||
arguments: [{ content: '_ctx.test' }, '["stop"]']
|
||||
},
|
||||
options: createObjectMatcher({
|
||||
capture: { content: 'true', isStatic: false },
|
||||
passive: { content: 'true', isStatic: false }
|
||||
})
|
||||
})
|
||||
key: {
|
||||
content: `onClick.capture.passive`
|
||||
},
|
||||
value: {
|
||||
callee: V_ON_WITH_MODIFIERS,
|
||||
arguments: [{ content: '_ctx.test' }, '["stop"]']
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('should wrap keys guard for keyboard events or dynamic events', () => {
|
||||
const {
|
||||
props: [prop]
|
||||
} = parseWithVOn(`<div @keyDown.stop.capture.ctrl.a="test"/>`, {
|
||||
} = parseWithVOn(`<div @keydown.stop.capture.ctrl.a="test"/>`, {
|
||||
prefixIdentifiers: true
|
||||
})
|
||||
expect(prop).toMatchObject({
|
||||
type: NodeTypes.JS_PROPERTY,
|
||||
value: createObjectMatcher({
|
||||
handler: {
|
||||
callee: V_ON_WITH_KEYS,
|
||||
arguments: [
|
||||
{
|
||||
callee: V_ON_WITH_MODIFIERS,
|
||||
arguments: [{ content: '_ctx.test' }, '["stop","ctrl"]']
|
||||
},
|
||||
'["a"]'
|
||||
]
|
||||
},
|
||||
options: createObjectMatcher({
|
||||
capture: { content: 'true', isStatic: false }
|
||||
})
|
||||
})
|
||||
key: {
|
||||
content: `onKeydown.capture`
|
||||
},
|
||||
value: {
|
||||
callee: V_ON_WITH_KEYS,
|
||||
arguments: [
|
||||
{
|
||||
callee: V_ON_WITH_MODIFIERS,
|
||||
arguments: [{ content: '_ctx.test' }, '["stop","ctrl"]']
|
||||
},
|
||||
'["a"]'
|
||||
]
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -206,9 +200,21 @@ describe('compiler-dom: transform v-on', () => {
|
||||
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||
children: [
|
||||
`(`,
|
||||
{ children: [`"on" + (`, { content: 'event' }, `)`] },
|
||||
`).toLowerCase() === "onclick" ? "onContextmenu" : (`,
|
||||
{ children: [`"on" + (`, { content: 'event' }, `)`] },
|
||||
{
|
||||
children: [
|
||||
`"on" + _${helperNameMap[CAPITALIZE]}(`,
|
||||
{ content: 'event' },
|
||||
`)`
|
||||
]
|
||||
},
|
||||
`) === "onClick" ? "onContextmenu" : (`,
|
||||
{
|
||||
children: [
|
||||
`"on" + _${helperNameMap[CAPITALIZE]}(`,
|
||||
{ content: 'event' },
|
||||
`)`
|
||||
]
|
||||
},
|
||||
`)`
|
||||
]
|
||||
})
|
||||
@@ -232,9 +238,21 @@ describe('compiler-dom: transform v-on', () => {
|
||||
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||
children: [
|
||||
`(`,
|
||||
{ children: [`"on" + (`, { content: 'event' }, `)`] },
|
||||
`).toLowerCase() === "onclick" ? "onMouseup" : (`,
|
||||
{ children: [`"on" + (`, { content: 'event' }, `)`] },
|
||||
{
|
||||
children: [
|
||||
`"on" + _${helperNameMap[CAPITALIZE]}(`,
|
||||
{ content: 'event' },
|
||||
`)`
|
||||
]
|
||||
},
|
||||
`) === "onClick" ? "onMouseup" : (`,
|
||||
{
|
||||
children: [
|
||||
`"on" + _${helperNameMap[CAPITALIZE]}(`,
|
||||
{ content: 'event' },
|
||||
`)`
|
||||
]
|
||||
},
|
||||
`)`
|
||||
]
|
||||
})
|
||||
@@ -254,24 +272,17 @@ describe('compiler-dom: transform v-on', () => {
|
||||
expect((root as any).children[0].codegenNode.patchFlag).toBe(
|
||||
genFlagText(PatchFlags.HYDRATE_EVENTS)
|
||||
)
|
||||
expect(prop.value).toMatchObject({
|
||||
type: NodeTypes.JS_CACHE_EXPRESSION,
|
||||
index: 1,
|
||||
expect(prop).toMatchObject({
|
||||
key: {
|
||||
content: `onKeyup.capture`
|
||||
},
|
||||
value: {
|
||||
type: NodeTypes.JS_OBJECT_EXPRESSION,
|
||||
properties: [
|
||||
{
|
||||
key: { content: 'handler' },
|
||||
value: {
|
||||
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||
callee: V_ON_WITH_KEYS
|
||||
}
|
||||
},
|
||||
{
|
||||
key: { content: 'options' },
|
||||
value: { type: NodeTypes.JS_OBJECT_EXPRESSION }
|
||||
}
|
||||
]
|
||||
type: NodeTypes.JS_CACHE_EXPRESSION,
|
||||
index: 1,
|
||||
value: {
|
||||
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||
callee: V_ON_WITH_KEYS
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
DirectiveTransform,
|
||||
createObjectProperty,
|
||||
createCallExpression,
|
||||
createObjectExpression,
|
||||
createSimpleExpression,
|
||||
NodeTypes,
|
||||
createCompoundExpression,
|
||||
@@ -80,7 +79,7 @@ const transformClick = (key: ExpressionNode, event: string) => {
|
||||
? createCompoundExpression([
|
||||
`(`,
|
||||
key,
|
||||
`).toLowerCase() === "onclick" ? "${event}" : (`,
|
||||
`) === "onClick" ? "${event}" : (`,
|
||||
key,
|
||||
`)`
|
||||
])
|
||||
@@ -126,20 +125,16 @@ export const transformOn: DirectiveTransform = (dir, node, context) => {
|
||||
}
|
||||
|
||||
if (eventOptionModifiers.length) {
|
||||
handlerExp = createObjectExpression([
|
||||
createObjectProperty('handler', handlerExp),
|
||||
createObjectProperty(
|
||||
'options',
|
||||
createObjectExpression(
|
||||
eventOptionModifiers.map(modifier =>
|
||||
createObjectProperty(
|
||||
modifier,
|
||||
createSimpleExpression('true', false)
|
||||
)
|
||||
)
|
||||
key = isStaticExp(key)
|
||||
? createSimpleExpression(
|
||||
`${key.content}.${eventOptionModifiers.join(`.`)}`,
|
||||
true
|
||||
)
|
||||
)
|
||||
])
|
||||
: createCompoundExpression([
|
||||
`(`,
|
||||
key,
|
||||
`) + ".${eventOptionModifiers.join(`.`)}"`
|
||||
])
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user