refactor: cache calls to performance.now
This commit is contained in:
parent
360a10fff2
commit
16270edda5
@ -1,5 +1,3 @@
|
|||||||
import { isChrome } from '../ua'
|
|
||||||
|
|
||||||
interface Invoker extends Function {
|
interface Invoker extends Function {
|
||||||
value: EventValue
|
value: EventValue
|
||||||
lastUpdated?: number
|
lastUpdated?: number
|
||||||
@ -9,6 +7,23 @@ type EventValue = (Function | Function[]) & {
|
|||||||
invoker?: Invoker | null
|
invoker?: Invoker | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// async edge case fix requires storing an event listener's attach timestamp
|
||||||
|
// to avoid the overhead of repeatedly calling performance.now(), we cache
|
||||||
|
// and use the same timestamp for all event listners attached in the same tick.
|
||||||
|
let cachedNow: number = 0
|
||||||
|
const p = Promise.resolve()
|
||||||
|
|
||||||
|
function getNow() {
|
||||||
|
if (cachedNow) {
|
||||||
|
return cachedNow
|
||||||
|
} else {
|
||||||
|
p.then(() => {
|
||||||
|
cachedNow = 0
|
||||||
|
})
|
||||||
|
return (cachedNow = performance.now())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function patchEvent(
|
export function patchEvent(
|
||||||
el: Element,
|
el: Element,
|
||||||
name: string,
|
name: string,
|
||||||
@ -21,9 +36,7 @@ export function patchEvent(
|
|||||||
;(prevValue as EventValue).invoker = null
|
;(prevValue as EventValue).invoker = null
|
||||||
invoker.value = nextValue
|
invoker.value = nextValue
|
||||||
nextValue.invoker = invoker
|
nextValue.invoker = invoker
|
||||||
if (isChrome) {
|
invoker.lastUpdated = getNow()
|
||||||
invoker.lastUpdated = performance.now()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
el.addEventListener(name, createInvoker(nextValue))
|
el.addEventListener(name, createInvoker(nextValue))
|
||||||
}
|
}
|
||||||
@ -38,20 +51,18 @@ function createInvoker(value: any) {
|
|||||||
}) as any
|
}) as any
|
||||||
invoker.value = value
|
invoker.value = value
|
||||||
value.invoker = invoker
|
value.invoker = invoker
|
||||||
if (isChrome) {
|
invoker.lastUpdated = getNow()
|
||||||
invoker.lastUpdated = performance.now()
|
|
||||||
}
|
|
||||||
return invoker
|
return invoker
|
||||||
}
|
}
|
||||||
|
|
||||||
function invokeEvents(e: Event, value: EventValue, lastUpdated: number) {
|
function invokeEvents(e: Event, value: EventValue, lastUpdated: number) {
|
||||||
// async edge case #6566: inner click event triggers patch, event handler
|
// async edge case #6566: inner click event triggers patch, event handler
|
||||||
// attached to outer element during patch, and triggered again. This only
|
// attached to outer element during patch, and triggered again. This
|
||||||
// happens in Chrome as it fires microtask ticks between event propagation.
|
// happens because browsers fire microtask ticks between event propagation.
|
||||||
// the solution is simple: we save the timestamp when a handler is attached,
|
// the solution is simple: we save the timestamp when a handler is attached,
|
||||||
// and the handler would only fire if the event passed to it was fired
|
// and the handler would only fire if the event passed to it was fired
|
||||||
// AFTER it was attached.
|
// AFTER it was attached.
|
||||||
if (isChrome && e.timeStamp < lastUpdated) {
|
if (e.timeStamp < lastUpdated) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
export const UA = window.navigator.userAgent.toLowerCase()
|
|
||||||
export const isEdge = UA.indexOf('edge/') > 0
|
|
||||||
export const isChrome = /chrome\/\d+/.test(UA) && !isEdge
|
|
Loading…
Reference in New Issue
Block a user