From e394493f71713684747f17588ec09eab2f2ea768 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 8 Dec 2018 20:05:59 -0500 Subject: [PATCH] more efficient event listeners --- src/compile/render-dom/Block.ts | 26 +++++++++++++++++ .../render-dom/wrappers/Element/index.ts | 8 ++--- .../wrappers/shared/addEventHandlers.ts | 16 +++------- src/internal/dom.js | 5 +--- test/js/samples/event-modifiers/expected.js | 19 ++++++------ test/js/samples/input-files/expected.js | 8 ++--- test/js/samples/input-range/expected.js | 14 +++++---- .../input-without-blowback-guard/expected.js | 8 ++--- test/js/samples/media-bindings/expected.js | 29 +++++++++---------- 9 files changed, 71 insertions(+), 62 deletions(-) diff --git a/src/compile/render-dom/Block.ts b/src/compile/render-dom/Block.ts index 12b7cca061..8a205e25fd 100644 --- a/src/compile/render-dom/Block.ts +++ b/src/compile/render-dom/Block.ts @@ -47,6 +47,8 @@ export default class Block { destroy: CodeBuilder; }; + event_listeners: string[] = []; + maintainContext: boolean; hasAnimation: boolean; hasIntros: boolean; @@ -234,6 +236,30 @@ export default class Block { this.builders.mount.addLine(`${this.autofocus}.focus();`); } + if (this.event_listeners.length > 0) { + this.addVariable('#dispose'); + + if (this.event_listeners.length === 1) { + this.builders.hydrate.addLine( + `#dispose = ${this.event_listeners[0]};` + ); + + this.builders.destroy.addLine( + `#dispose();` + ) + } else { + this.builders.hydrate.addBlock(deindent` + #dispose = [ + ${this.event_listeners.join(',\n')} + ]; + `); + + this.builders.destroy.addLine( + `@run_all(#dispose);` + ); + } + } + const properties = new CodeBuilder(); if (localKey) { diff --git a/src/compile/render-dom/wrappers/Element/index.ts b/src/compile/render-dom/wrappers/Element/index.ts index 0f6e8fbe9e..91bfc8e410 100644 --- a/src/compile/render-dom/wrappers/Element/index.ts +++ b/src/compile/render-dom/wrappers/Element/index.ts @@ -503,12 +503,8 @@ export default class ElementWrapper extends Wrapper { `${resize_listener}.cancel();` ); } else { - block.builders.hydrate.addLine( - `@addListener(${this.var}, "${name}", ${callee});` - ); - - block.builders.destroy.addLine( - `@removeListener(${this.var}, "${name}", ${callee});` + block.event_listeners.push( + `@addListener(${this.var}, "${name}", ${callee})` ); } }); diff --git a/src/compile/render-dom/wrappers/shared/addEventHandlers.ts b/src/compile/render-dom/wrappers/shared/addEventHandlers.ts index 9083949c76..7a82f72860 100644 --- a/src/compile/render-dom/wrappers/shared/addEventHandlers.ts +++ b/src/compile/render-dom/wrappers/shared/addEventHandlers.ts @@ -20,20 +20,12 @@ export default function addEventHandlers( ? 'true' : `{ ${opts.map(opt => `${opt}: true`).join(', ')} }`; - block.builders.hydrate.addLine( - `@addListener(${target}, "${handler.name}", ${snippet}, ${optString});` - ); - - block.builders.destroy.addLine( - `@removeListener(${target}, "${handler.name}", ${snippet}, ${optString});` + block.event_listeners.push( + `@addListener(${target}, "${handler.name}", ${snippet}, ${optString})` ); } else { - block.builders.hydrate.addLine( - `@addListener(${target}, "${handler.name}", ${snippet});` - ); - - block.builders.destroy.addLine( - `@removeListener(${target}, "${handler.name}", ${snippet});` + block.event_listeners.push( + `@addListener(${target}, "${handler.name}", ${snippet})` ); } diff --git a/src/internal/dom.js b/src/internal/dom.js index 1411a7693e..abde45502d 100644 --- a/src/internal/dom.js +++ b/src/internal/dom.js @@ -75,10 +75,7 @@ export function createComment() { export function addListener(node, event, handler, options) { node.addEventListener(event, handler, options); -} - -export function removeListener(node, event, handler, options) { - node.removeEventListener(event, handler, options); + return () => node.removeEventListener(event, handler, options); } export function setAttribute(node, attribute, value) { diff --git a/test/js/samples/event-modifiers/expected.js b/test/js/samples/event-modifiers/expected.js index c38d2fea71..e582c1b030 100644 --- a/test/js/samples/event-modifiers/expected.js +++ b/test/js/samples/event-modifiers/expected.js @@ -1,8 +1,8 @@ /* generated by Svelte vX.Y.Z-alpha1 */ -import { SvelteComponent as SvelteComponent_1, addListener, append, createElement, createText, detachNode, init, insert, noop, removeListener, run, safe_not_equal } from "svelte/internal.js"; +import { SvelteComponent as SvelteComponent_1, addListener, append, createElement, createText, detachNode, init, insert, noop, run, run_all, safe_not_equal } from "svelte/internal.js"; function create_fragment(component, ctx) { - var div, button0, text1, button1, text3, button2, current; + var div, button0, text1, button1, text3, button2, current, dispose; function click_handler(event) { event.stopPropagation(); @@ -21,10 +21,12 @@ function create_fragment(component, ctx) { text3 = createText("\n\t"); button2 = createElement("button"); button2.textContent = "or me!"; - addListener(button0, click_handler); - addListener(button1, "click", handleClick, { once: true, capture: true }); - addListener(button2, "click", handleClick, true); - addListener(div, "touchstart", handleTouchstart, { passive: true }); + dispose = [ + addListener(button0, click_handler), + addListener(button1, "click", handleClick, { once: true, capture: true }), + addListener(button2, "click", handleClick, true), + addListener(div, "touchstart", handleTouchstart, { passive: true }) + ]; }, m(target, anchor) { @@ -51,10 +53,7 @@ function create_fragment(component, ctx) { detachNode(div); } - removeListener(button0, click_handler); - removeListener(button1, "click", handleClick, { once: true, capture: true }); - removeListener(button2, "click", handleClick, true); - removeListener(div, "touchstart", handleTouchstart, { passive: true }); + run_all(dispose); } }; } diff --git a/test/js/samples/input-files/expected.js b/test/js/samples/input-files/expected.js index ba90b94e58..dc3d796be3 100644 --- a/test/js/samples/input-files/expected.js +++ b/test/js/samples/input-files/expected.js @@ -1,15 +1,15 @@ /* generated by Svelte vX.Y.Z-alpha1 */ -import { SvelteComponent as SvelteComponent_1, addListener, createElement, detachNode, flush, init, insert, removeListener, run, safe_not_equal, setAttribute } from "svelte/internal.js"; +import { SvelteComponent as SvelteComponent_1, addListener, createElement, detachNode, flush, init, insert, run, safe_not_equal, setAttribute } from "svelte/internal.js"; function create_fragment(component, ctx) { - var input, input_updating = false, current; + var input, input_updating = false, current, dispose; return { c() { input = createElement("input"); - addListener(input, "input", ctx.input_input_handler); setAttribute(input, "type", "file"); input.multiple = true; + dispose = addListener(input, "input", ctx.input_input_handler); }, m(target, anchor) { @@ -36,7 +36,7 @@ function create_fragment(component, ctx) { detachNode(input); } - removeListener(input, "input", ctx.input_input_handler); + dispose(); } }; } diff --git a/test/js/samples/input-range/expected.js b/test/js/samples/input-range/expected.js index 10d2325769..cafd2369d4 100644 --- a/test/js/samples/input-range/expected.js +++ b/test/js/samples/input-range/expected.js @@ -1,15 +1,18 @@ /* generated by Svelte vX.Y.Z-alpha1 */ -import { SvelteComponent as SvelteComponent_1, addListener, createElement, detachNode, flush, init, insert, removeListener, run, safe_not_equal, setAttribute, toNumber } from "svelte/internal.js"; +import { SvelteComponent as SvelteComponent_1, addListener, createElement, detachNode, flush, init, insert, run, run_all, safe_not_equal, setAttribute, toNumber } from "svelte/internal.js"; function create_fragment(component, ctx) { - var input, current; + var input, current, dispose; return { c() { input = createElement("input"); - addListener(input, "change", ctx.input_change_input_handler); - addListener(input, "input", ctx.input_change_input_handler); setAttribute(input, "type", "range"); + + dispose = [ + addListener(input, "change", ctx.input_change_input_handler), + addListener(input, "input", ctx.input_change_input_handler) + ]; }, m(target, anchor) { @@ -36,8 +39,7 @@ function create_fragment(component, ctx) { detachNode(input); } - removeListener(input, "change", ctx.input_change_input_handler); - removeListener(input, "input", ctx.input_change_input_handler); + run_all(dispose); } }; } diff --git a/test/js/samples/input-without-blowback-guard/expected.js b/test/js/samples/input-without-blowback-guard/expected.js index 41f6896905..3f829fac96 100644 --- a/test/js/samples/input-without-blowback-guard/expected.js +++ b/test/js/samples/input-without-blowback-guard/expected.js @@ -1,14 +1,14 @@ /* generated by Svelte vX.Y.Z-alpha1 */ -import { SvelteComponent as SvelteComponent_1, addListener, createElement, detachNode, flush, init, insert, removeListener, run, safe_not_equal, setAttribute } from "svelte/internal.js"; +import { SvelteComponent as SvelteComponent_1, addListener, createElement, detachNode, flush, init, insert, run, safe_not_equal, setAttribute } from "svelte/internal.js"; function create_fragment(component, ctx) { - var input, current; + var input, current, dispose; return { c() { input = createElement("input"); - addListener(input, "change", ctx.input_change_handler); setAttribute(input, "type", "checkbox"); + dispose = addListener(input, "change", ctx.input_change_handler); }, m(target, anchor) { @@ -35,7 +35,7 @@ function create_fragment(component, ctx) { detachNode(input); } - removeListener(input, "change", ctx.input_change_handler); + dispose(); } }; } diff --git a/test/js/samples/media-bindings/expected.js b/test/js/samples/media-bindings/expected.js index 83ca52634a..4879439d17 100644 --- a/test/js/samples/media-bindings/expected.js +++ b/test/js/samples/media-bindings/expected.js @@ -1,23 +1,26 @@ /* generated by Svelte vX.Y.Z-alpha1 */ -import { SvelteComponent as SvelteComponent_1, addListener, add_render_callback, createElement, detachNode, flush, init, insert, removeListener, run, safe_not_equal, timeRangesToArray } from "svelte/internal.js"; +import { SvelteComponent as SvelteComponent_1, addListener, add_render_callback, createElement, detachNode, flush, init, insert, run, run_all, safe_not_equal, timeRangesToArray } from "svelte/internal.js"; function create_fragment(component, ctx) { - var audio, audio_is_paused = true, audio_updating = false, audio_animationframe, current; + var audio, audio_is_paused = true, audio_updating = false, audio_animationframe, current, dispose; return { c() { audio = createElement("audio"); - addListener(audio, "timeupdate", ctx.audio_timeupdate_handler); if (ctx.played === void 0 || ctx.currentTime === void 0) add_render_callback(() => ctx.audio_timeupdate_handler.call(audio)); - addListener(audio, "durationchange", ctx.audio_durationchange_handler); if (ctx.duration === void 0) add_render_callback(() => ctx.audio_durationchange_handler.call(audio)); - addListener(audio, "play", ctx.audio_play_pause_handler); - addListener(audio, "pause", ctx.audio_play_pause_handler); - addListener(audio, "progress", ctx.audio_progress_handler); if (ctx.buffered === void 0) add_render_callback(() => ctx.audio_progress_handler.call(audio)); - addListener(audio, "loadedmetadata", ctx.audio_loadedmetadata_handler); if (ctx.buffered === void 0 || ctx.seekable === void 0) add_render_callback(() => ctx.audio_loadedmetadata_handler.call(audio)); - addListener(audio, "volumechange", ctx.audio_volumechange_handler); + + dispose = [ + addListener(audio, "timeupdate", ctx.audio_timeupdate_handler), + addListener(audio, "durationchange", ctx.audio_durationchange_handler), + addListener(audio, "play", ctx.audio_play_pause_handler), + addListener(audio, "pause", ctx.audio_play_pause_handler), + addListener(audio, "progress", ctx.audio_progress_handler), + addListener(audio, "loadedmetadata", ctx.audio_loadedmetadata_handler), + addListener(audio, "volumechange", ctx.audio_volumechange_handler) + ]; }, m(target, anchor) { @@ -46,13 +49,7 @@ function create_fragment(component, ctx) { detachNode(audio); } - removeListener(audio, "timeupdate", ctx.audio_timeupdate_handler); - removeListener(audio, "durationchange", ctx.audio_durationchange_handler); - removeListener(audio, "play", ctx.audio_play_pause_handler); - removeListener(audio, "pause", ctx.audio_play_pause_handler); - removeListener(audio, "progress", ctx.audio_progress_handler); - removeListener(audio, "loadedmetadata", ctx.audio_loadedmetadata_handler); - removeListener(audio, "volumechange", ctx.audio_volumechange_handler); + run_all(dispose); } }; }