From 16270edda5efc8ddd80961a7368fd92483c3c606 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 23 Jan 2019 18:52:05 -0500 Subject: [PATCH] refactor: cache calls to performance.now --- packages/runtime-dom/src/modules/events.ts | 33 ++++++++++++++-------- packages/runtime-dom/src/ua.ts | 3 -- 2 files changed, 22 insertions(+), 14 deletions(-) delete mode 100644 packages/runtime-dom/src/ua.ts diff --git a/packages/runtime-dom/src/modules/events.ts b/packages/runtime-dom/src/modules/events.ts index 77fbce8c..11b37454 100644 --- a/packages/runtime-dom/src/modules/events.ts +++ b/packages/runtime-dom/src/modules/events.ts @@ -1,5 +1,3 @@ -import { isChrome } from '../ua' - interface Invoker extends Function { value: EventValue lastUpdated?: number @@ -9,6 +7,23 @@ type EventValue = (Function | Function[]) & { 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( el: Element, name: string, @@ -21,9 +36,7 @@ export function patchEvent( ;(prevValue as EventValue).invoker = null invoker.value = nextValue nextValue.invoker = invoker - if (isChrome) { - invoker.lastUpdated = performance.now() - } + invoker.lastUpdated = getNow() } else { el.addEventListener(name, createInvoker(nextValue)) } @@ -38,20 +51,18 @@ function createInvoker(value: any) { }) as any invoker.value = value value.invoker = invoker - if (isChrome) { - invoker.lastUpdated = performance.now() - } + invoker.lastUpdated = getNow() return invoker } function invokeEvents(e: Event, value: EventValue, lastUpdated: number) { // async edge case #6566: inner click event triggers patch, event handler - // attached to outer element during patch, and triggered again. This only - // happens in Chrome as it fires microtask ticks between event propagation. + // attached to outer element during patch, and triggered again. This + // happens because browsers fire microtask ticks between event propagation. // 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 // AFTER it was attached. - if (isChrome && e.timeStamp < lastUpdated) { + if (e.timeStamp < lastUpdated) { return } diff --git a/packages/runtime-dom/src/ua.ts b/packages/runtime-dom/src/ua.ts deleted file mode 100644 index 62bfee86..00000000 --- a/packages/runtime-dom/src/ua.ts +++ /dev/null @@ -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