perf(core): minor improvements to listener instructions (#41807)

Makes the following improvements to the listener instructions to make them slightly smaller and more memory-efficient.

1. Removes the default value from the `useCapture` parameter since it generates more code than just castint to `false`.
2. Removes the `useCapture` and `eventTargetResolver` parameters from `ɵɵsyntheticHostListener` since they won't be generated by the compiler, as far as I can tell.
3. Makes it so that we don't have to return a target name from a `GlobalTargetResolver`. This allows us to save on some memory, because we can return a reference to the target without having to wrap it in an object literal.

DEPRECATIONS:
`EventManagerPlugin.getGlobalEventTarget` is now deprecated and won't be called from Ivy code anymore. Global events will go through `addEventListener`.

PR Close #41807
This commit is contained in:
Kristiyan Kostadinov 2021-04-29 22:03:51 +02:00 committed by Misko Hevery
parent 2dd96e08ae
commit 6581a1b48d
8 changed files with 18 additions and 22 deletions

View File

@ -32,7 +32,7 @@ export declare const EVENT_MANAGER_PLUGINS: InjectionToken<ɵangular_packages_pl
export declare class EventManager { export declare class EventManager {
constructor(plugins: ɵangular_packages_platform_browser_platform_browser_g[], _zone: NgZone); constructor(plugins: ɵangular_packages_platform_browser_platform_browser_g[], _zone: NgZone);
addEventListener(element: HTMLElement, eventName: string, handler: Function): Function; addEventListener(element: HTMLElement, eventName: string, handler: Function): Function;
addGlobalEventListener(target: string, eventName: string, handler: Function): Function; /** @deprecated */ addGlobalEventListener(target: string, eventName: string, handler: Function): Function;
getZone(): NgZone; getZone(): NgZone;
} }

View File

@ -8,7 +8,6 @@
import {assertIndexInRange} from '../../util/assert'; import {assertIndexInRange} from '../../util/assert';
import {EMPTY_OBJ} from '../../util/empty';
import {isObservable} from '../../util/lang'; import {isObservable} from '../../util/lang';
import {PropertyAliasValue, TNode, TNodeFlags, TNodeType} from '../interfaces/node'; import {PropertyAliasValue, TNode, TNodeFlags, TNodeType} from '../interfaces/node';
import {GlobalTargetResolver, isProceduralRenderer, Renderer3} from '../interfaces/renderer'; import {GlobalTargetResolver, isProceduralRenderer, Renderer3} from '../interfaces/renderer';
@ -39,13 +38,14 @@ import {getOrCreateLViewCleanup, getOrCreateTViewCleanup, handleError, loadCompo
* @codeGenApi * @codeGenApi
*/ */
export function ɵɵlistener( export function ɵɵlistener(
eventName: string, listenerFn: (e?: any) => any, useCapture = false, eventName: string, listenerFn: (e?: any) => any, useCapture?: boolean,
eventTargetResolver?: GlobalTargetResolver): typeof ɵɵlistener { eventTargetResolver?: GlobalTargetResolver): typeof ɵɵlistener {
const lView = getLView(); const lView = getLView();
const tView = getTView(); const tView = getTView();
const tNode = getCurrentTNode()!; const tNode = getCurrentTNode()!;
listenerInternal( listenerInternal(
tView, lView, lView[RENDERER], tNode, eventName, listenerFn, useCapture, eventTargetResolver); tView, lView, lView[RENDERER], tNode, eventName, listenerFn, !!useCapture,
eventTargetResolver);
return ɵɵlistener; return ɵɵlistener;
} }
@ -71,15 +71,13 @@ export function ɵɵlistener(
* @codeGenApi * @codeGenApi
*/ */
export function ɵɵsyntheticHostListener( export function ɵɵsyntheticHostListener(
eventName: string, listenerFn: (e?: any) => any, useCapture = false, eventName: string, listenerFn: (e?: any) => any): typeof ɵɵsyntheticHostListener {
eventTargetResolver?: GlobalTargetResolver): typeof ɵɵsyntheticHostListener {
const tNode = getCurrentTNode()!; const tNode = getCurrentTNode()!;
const lView = getLView(); const lView = getLView();
const tView = getTView(); const tView = getTView();
const currentDef = getCurrentDirectiveDef(tView.data); const currentDef = getCurrentDirectiveDef(tView.data);
const renderer = loadComponentRenderer(currentDef, tNode, lView); const renderer = loadComponentRenderer(currentDef, tNode, lView);
listenerInternal( listenerInternal(tView, lView, renderer, tNode, eventName, listenerFn, false);
tView, lView, renderer, tNode, eventName, listenerFn, useCapture, eventTargetResolver);
return ɵɵsyntheticHostListener; return ɵɵsyntheticHostListener;
} }
@ -117,7 +115,7 @@ function findExistingListener(
function listenerInternal( function listenerInternal(
tView: TView, lView: LView, renderer: Renderer3, tNode: TNode, eventName: string, tView: TView, lView: LView, renderer: Renderer3, tNode: TNode, eventName: string,
listenerFn: (e?: any) => any, useCapture = false, listenerFn: (e?: any) => any, useCapture: boolean,
eventTargetResolver?: GlobalTargetResolver): void { eventTargetResolver?: GlobalTargetResolver): void {
const isTNodeDirectiveHost = isDirectiveHost(tNode); const isTNodeDirectiveHost = isDirectiveHost(tNode);
const firstCreatePass = tView.firstCreatePass; const firstCreatePass = tView.firstCreatePass;
@ -136,11 +134,10 @@ function listenerInternal(
// add native event listener - applicable to elements only // add native event listener - applicable to elements only
if (tNode.type & TNodeType.AnyRNode) { if (tNode.type & TNodeType.AnyRNode) {
const native = getNativeByTNode(tNode, lView) as RElement; const native = getNativeByTNode(tNode, lView) as RElement;
const resolved = eventTargetResolver ? eventTargetResolver(native) : EMPTY_OBJ as any; const target = eventTargetResolver ? eventTargetResolver(native) : native;
const target = resolved.target || native;
const lCleanupIndex = lCleanup.length; const lCleanupIndex = lCleanup.length;
const idxOrTargetGetter = eventTargetResolver ? const idxOrTargetGetter = eventTargetResolver ?
(_lView: LView) => eventTargetResolver(unwrapRNode(_lView[tNode.index])).target : (_lView: LView) => eventTargetResolver(unwrapRNode(_lView[tNode.index])) :
tNode.index; tNode.index;
// In order to match current behavior, native DOM event listeners must be added for all // In order to match current behavior, native DOM event listeners must be added for all
@ -176,11 +173,8 @@ function listenerInternal(
(<any>existingListener).__ngLastListenerFn__ = listenerFn; (<any>existingListener).__ngLastListenerFn__ = listenerFn;
processOutputs = false; processOutputs = false;
} else { } else {
// The first argument of `listen` function in Procedural Renderer is:
// - either a target name (as a string) in case of global target (window, document, body)
// - or element reference (in all other cases)
listenerFn = wrapListener(tNode, lView, context, listenerFn, false /** preventDefault */); listenerFn = wrapListener(tNode, lView, context, listenerFn, false /** preventDefault */);
const cleanupFn = renderer.listen(resolved.name || target, eventName, listenerFn); const cleanupFn = renderer.listen(target as RElement, eventName, listenerFn);
ngDevMode && ngDevMode.rendererAddEventListener++; ngDevMode && ngDevMode.rendererAddEventListener++;
lCleanup.push(listenerFn, cleanupFn); lCleanup.push(listenerFn, cleanupFn);

View File

@ -30,9 +30,7 @@ export type Renderer3 = ObjectOrientedRenderer3|ProceduralRenderer3;
export type GlobalTargetName = 'document'|'window'|'body'; export type GlobalTargetName = 'document'|'window'|'body';
export type GlobalTargetResolver = (element: any) => { export type GlobalTargetResolver = (element: any) => EventTarget;
name: GlobalTargetName, target: EventTarget
};
/** /**
* Object Oriented style of API needed to create elements and text nodes. * Object Oriented style of API needed to create elements and text nodes.

View File

@ -23,7 +23,7 @@ export const defaultScheduler =
* @codeGenApi * @codeGenApi
*/ */
export function ɵɵresolveWindow(element: RElement&{ownerDocument: Document}) { export function ɵɵresolveWindow(element: RElement&{ownerDocument: Document}) {
return {name: 'window', target: element.ownerDocument.defaultView}; return element.ownerDocument.defaultView;
} }
/** /**
@ -31,7 +31,7 @@ export function ɵɵresolveWindow(element: RElement&{ownerDocument: Document}) {
* @codeGenApi * @codeGenApi
*/ */
export function ɵɵresolveDocument(element: RElement&{ownerDocument: Document}) { export function ɵɵresolveDocument(element: RElement&{ownerDocument: Document}) {
return {name: 'document', target: element.ownerDocument}; return element.ownerDocument;
} }
/** /**
@ -39,7 +39,7 @@ export function ɵɵresolveDocument(element: RElement&{ownerDocument: Document})
* @codeGenApi * @codeGenApi
*/ */
export function ɵɵresolveBody(element: RElement&{ownerDocument: Document}) { export function ɵɵresolveBody(element: RElement&{ownerDocument: Document}) {
return {name: 'body', target: element.ownerDocument.body}; return element.ownerDocument.body;
} }
/** /**

View File

@ -57,6 +57,7 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
return node instanceof DocumentFragment; return node instanceof DocumentFragment;
} }
/** @deprecated No longer being used in Ivy code. To be removed in version 14. */
getGlobalEventTarget(doc: Document, target: string): EventTarget|null { getGlobalEventTarget(doc: Document, target: string): EventTarget|null {
if (target === 'window') { if (target === 'window') {
return window; return window;

View File

@ -58,6 +58,7 @@ export class EventManager {
* @param handler A function to call when the notification occurs. Receives the * @param handler A function to call when the notification occurs. Receives the
* event object as an argument. * event object as an argument.
* @returns A callback function that can be used to remove the handler. * @returns A callback function that can be used to remove the handler.
* @deprecated No longer being used in Ivy code. To be removed in version 14.
*/ */
addGlobalEventListener(target: string, eventName: string, handler: Function): Function { addGlobalEventListener(target: string, eventName: string, handler: Function): Function {
const plugin = this._findPluginFor(eventName); const plugin = this._findPluginFor(eventName);

View File

@ -62,6 +62,7 @@ export class DominoAdapter extends BrowserDomAdapter {
return node.shadowRoot == node; return node.shadowRoot == node;
} }
/** @deprecated No longer being used in Ivy code. To be removed in version 14. */
getGlobalEventTarget(doc: Document, target: string): EventTarget|null { getGlobalEventTarget(doc: Document, target: string): EventTarget|null {
if (target === 'window') { if (target === 'window') {
return doc.defaultView; return doc.defaultView;

View File

@ -22,6 +22,7 @@ export class ServerEventManagerPlugin /* extends EventManagerPlugin which is pri
return getDOM().onAndCancel(element, eventName, handler); return getDOM().onAndCancel(element, eventName, handler);
} }
/** @deprecated No longer being used in Ivy code. To be removed in version 14. */
addGlobalEventListener(element: string, eventName: string, handler: Function): Function { addGlobalEventListener(element: string, eventName: string, handler: Function): Function {
const target: HTMLElement = getDOM().getGlobalEventTarget(this.doc, element); const target: HTMLElement = getDOM().getGlobalEventTarget(this.doc, element);
if (!target) { if (!target) {