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:
@@ -1,4 +1,4 @@
|
||||
import { EMPTY_OBJ, isArray } from '@vue/shared'
|
||||
import { isArray } from '@vue/shared'
|
||||
import {
|
||||
ComponentInternalInstance,
|
||||
callWithAsyncErrorHandling
|
||||
@@ -14,12 +14,6 @@ type EventValue = (Function | Function[]) & {
|
||||
invoker?: Invoker | null
|
||||
}
|
||||
|
||||
type EventValueWithOptions = {
|
||||
handler: EventValue
|
||||
options: AddEventListenerOptions
|
||||
invoker?: Invoker | null
|
||||
}
|
||||
|
||||
// Async edge case fix requires storing an event listener's attach timestamp.
|
||||
let _getNow: () => number = Date.now
|
||||
|
||||
@@ -67,52 +61,43 @@ export function removeEventListener(
|
||||
export function patchEvent(
|
||||
el: Element,
|
||||
rawName: string,
|
||||
prevValue: EventValueWithOptions | EventValue | null,
|
||||
nextValue: EventValueWithOptions | EventValue | null,
|
||||
prevValue: EventValue | null,
|
||||
nextValue: EventValue | null,
|
||||
instance: ComponentInternalInstance | null = null
|
||||
) {
|
||||
const name = rawName.slice(2).toLowerCase()
|
||||
const prevOptions = prevValue && 'options' in prevValue && prevValue.options
|
||||
const nextOptions = nextValue && 'options' in nextValue && nextValue.options
|
||||
const invoker = prevValue && prevValue.invoker
|
||||
const value =
|
||||
nextValue && 'handler' in nextValue ? nextValue.handler : nextValue
|
||||
|
||||
if (prevOptions || nextOptions) {
|
||||
const prev = prevOptions || EMPTY_OBJ
|
||||
const next = nextOptions || EMPTY_OBJ
|
||||
if (
|
||||
prev.capture !== next.capture ||
|
||||
prev.passive !== next.passive ||
|
||||
prev.once !== next.once
|
||||
) {
|
||||
if (invoker) {
|
||||
removeEventListener(el, name, invoker, prev)
|
||||
}
|
||||
if (nextValue && value) {
|
||||
const invoker = createInvoker(value, instance)
|
||||
nextValue.invoker = invoker
|
||||
addEventListener(el, name, invoker, next)
|
||||
}
|
||||
return
|
||||
if (nextValue && invoker) {
|
||||
// patch
|
||||
;(prevValue as EventValue).invoker = null
|
||||
invoker.value = nextValue
|
||||
nextValue.invoker = invoker
|
||||
} else {
|
||||
const [name, options] = parseName(rawName)
|
||||
if (nextValue) {
|
||||
addEventListener(el, name, createInvoker(nextValue, instance), options)
|
||||
} else if (invoker) {
|
||||
// remove
|
||||
removeEventListener(el, name, invoker, options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nextValue && value) {
|
||||
if (invoker) {
|
||||
;(prevValue as EventValue).invoker = null
|
||||
invoker.value = value
|
||||
nextValue.invoker = invoker
|
||||
} else {
|
||||
addEventListener(
|
||||
el,
|
||||
name,
|
||||
createInvoker(value, instance),
|
||||
nextOptions || void 0
|
||||
)
|
||||
}
|
||||
} else if (invoker) {
|
||||
removeEventListener(el, name, invoker, prevOptions || void 0)
|
||||
const optionsModifierRE = /\.(once|passive|capture)\b/g
|
||||
|
||||
function parseName(name: string): [string, EventListenerOptions | undefined] {
|
||||
name = name.slice(2).toLowerCase()
|
||||
if (optionsModifierRE.test(name)) {
|
||||
const options: EventListenerOptions = {}
|
||||
name = name.replace(
|
||||
optionsModifierRE,
|
||||
(_, key: keyof EventListenerOptions) => {
|
||||
options[key] = true
|
||||
return ''
|
||||
}
|
||||
)
|
||||
return [name, options]
|
||||
} else {
|
||||
return [name, undefined]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user