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 {
constructor(plugins: ɵangular_packages_platform_browser_platform_browser_g[], _zone: NgZone);
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;
}

View File

@ -8,7 +8,6 @@
import {assertIndexInRange} from '../../util/assert';
import {EMPTY_OBJ} from '../../util/empty';
import {isObservable} from '../../util/lang';
import {PropertyAliasValue, TNode, TNodeFlags, TNodeType} from '../interfaces/node';
import {GlobalTargetResolver, isProceduralRenderer, Renderer3} from '../interfaces/renderer';
@ -39,13 +38,14 @@ import {getOrCreateLViewCleanup, getOrCreateTViewCleanup, handleError, loadCompo
* @codeGenApi
*/
export function ɵɵlistener(
eventName: string, listenerFn: (e?: any) => any, useCapture = false,
eventName: string, listenerFn: (e?: any) => any, useCapture?: boolean,
eventTargetResolver?: GlobalTargetResolver): typeof ɵɵlistener {
const lView = getLView();
const tView = getTView();
const tNode = getCurrentTNode()!;
listenerInternal(
tView, lView, lView[RENDERER], tNode, eventName, listenerFn, useCapture, eventTargetResolver);
tView, lView, lView[RENDERER], tNode, eventName, listenerFn, !!useCapture,
eventTargetResolver);
return ɵɵlistener;
}
@ -71,15 +71,13 @@ export function ɵɵlistener(
* @codeGenApi
*/
export function ɵɵsyntheticHostListener(
eventName: string, listenerFn: (e?: any) => any, useCapture = false,
eventTargetResolver?: GlobalTargetResolver): typeof ɵɵsyntheticHostListener {
eventName: string, listenerFn: (e?: any) => any): typeof ɵɵsyntheticHostListener {
const tNode = getCurrentTNode()!;
const lView = getLView();
const tView = getTView();
const currentDef = getCurrentDirectiveDef(tView.data);
const renderer = loadComponentRenderer(currentDef, tNode, lView);
listenerInternal(
tView, lView, renderer, tNode, eventName, listenerFn, useCapture, eventTargetResolver);
listenerInternal(tView, lView, renderer, tNode, eventName, listenerFn, false);
return ɵɵsyntheticHostListener;
}
@ -117,7 +115,7 @@ function findExistingListener(
function listenerInternal(
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 {
const isTNodeDirectiveHost = isDirectiveHost(tNode);
const firstCreatePass = tView.firstCreatePass;
@ -136,11 +134,10 @@ function listenerInternal(
// add native event listener - applicable to elements only
if (tNode.type & TNodeType.AnyRNode) {
const native = getNativeByTNode(tNode, lView) as RElement;
const resolved = eventTargetResolver ? eventTargetResolver(native) : EMPTY_OBJ as any;
const target = resolved.target || native;
const target = eventTargetResolver ? eventTargetResolver(native) : native;
const lCleanupIndex = lCleanup.length;
const idxOrTargetGetter = eventTargetResolver ?
(_lView: LView) => eventTargetResolver(unwrapRNode(_lView[tNode.index])).target :
(_lView: LView) => eventTargetResolver(unwrapRNode(_lView[tNode.index])) :
tNode.index;
// 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;
processOutputs = false;
} 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 */);
const cleanupFn = renderer.listen(resolved.name || target, eventName, listenerFn);
const cleanupFn = renderer.listen(target as RElement, eventName, listenerFn);
ngDevMode && ngDevMode.rendererAddEventListener++;
lCleanup.push(listenerFn, cleanupFn);

View File

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

View File

@ -23,7 +23,7 @@ export const defaultScheduler =
* @codeGenApi
*/
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
*/
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
*/
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;
}
/** @deprecated No longer being used in Ivy code. To be removed in version 14. */
getGlobalEventTarget(doc: Document, target: string): EventTarget|null {
if (target === 'window') {
return window;

View File

@ -58,6 +58,7 @@ export class EventManager {
* @param handler A function to call when the notification occurs. Receives the
* event object as an argument.
* @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 {
const plugin = this._findPluginFor(eventName);

View File

@ -62,6 +62,7 @@ export class DominoAdapter extends BrowserDomAdapter {
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 {
if (target === 'window') {
return doc.defaultView;

View File

@ -22,6 +22,7 @@ export class ServerEventManagerPlugin /* extends EventManagerPlugin which is pri
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 {
const target: HTMLElement = getDOM().getGlobalEventTarget(this.doc, element);
if (!target) {