mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
refactor(devtools): remove MutationObserver and rename observer to execution hooks
This commit is contained in:
@@ -10,7 +10,7 @@ import {
|
||||
ComponentExplorerViewQuery,
|
||||
} from 'protocol';
|
||||
import { ComponentTreeNode, getLatestComponentState, queryDirectiveForest, updateState } from './component-tree';
|
||||
import { start as startProfiling, stop as stopProfiling } from './observer/capture';
|
||||
import { start as startProfiling, stop as stopProfiling } from './hooks/capture';
|
||||
import { serializeDirectiveState } from './state-serializer/state-serializer';
|
||||
import { ComponentInspector, ComponentInspectorOptions } from './component-inspector/component-inspector';
|
||||
import { setConsoleReference } from './set-console-reference';
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
appIsAngularIvy,
|
||||
} from './angular-check';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
import { disableTimingAPI, enableTimingAPI, initializeOrGetDirectiveForestObserver } from './observer';
|
||||
import { disableTimingAPI, enableTimingAPI, initializeOrGetDirectiveForestHooks } from './hooks';
|
||||
import { runOutsideAngular } from './utils';
|
||||
|
||||
export const subscribeToClientEvents = (messageBus: MessageBus<Events>): void => {
|
||||
@@ -51,7 +51,7 @@ export const subscribeToClientEvents = (messageBus: MessageBus<Events>): void =>
|
||||
// update requests, instead we want to request an update at most
|
||||
// every 250ms
|
||||
runOutsideAngular(() => {
|
||||
initializeOrGetDirectiveForestObserver()
|
||||
initializeOrGetDirectiveForestHooks()
|
||||
.changeDetection$.pipe(debounceTime(250))
|
||||
.subscribe(() => messageBus.emit('componentTreeDirty'));
|
||||
});
|
||||
@@ -71,18 +71,18 @@ const getLatestComponentExplorerViewCallback = (messageBus: MessageBus<Events>)
|
||||
) => {
|
||||
// We want to force re-indexing of the component tree.
|
||||
// Pressing the refresh button means the user saw stuck UI.
|
||||
initializeOrGetDirectiveForestObserver().indexForest();
|
||||
initializeOrGetDirectiveForestHooks().indexForest();
|
||||
if (!query) {
|
||||
messageBus.emit('latestComponentExplorerView', [
|
||||
{
|
||||
forest: prepareForestForSerialization(initializeOrGetDirectiveForestObserver().getDirectiveForest()),
|
||||
forest: prepareForestForSerialization(initializeOrGetDirectiveForestHooks().getDirectiveForest()),
|
||||
},
|
||||
]);
|
||||
return;
|
||||
}
|
||||
messageBus.emit('latestComponentExplorerView', [
|
||||
{
|
||||
forest: prepareForestForSerialization(initializeOrGetDirectiveForestObserver().getDirectiveForest()),
|
||||
forest: prepareForestForSerialization(initializeOrGetDirectiveForestHooks().getDirectiveForest()),
|
||||
properties: getLatestComponentState(query),
|
||||
},
|
||||
]);
|
||||
@@ -100,7 +100,7 @@ const stopProfilingCallback = (messageBus: MessageBus<Events>) => () => {
|
||||
};
|
||||
|
||||
const selectedComponentCallback = (position: ElementPosition) => {
|
||||
const node = queryDirectiveForest(position, initializeOrGetDirectiveForestObserver().getDirectiveForest());
|
||||
const node = queryDirectiveForest(position, initializeOrGetDirectiveForestHooks().getDirectiveForest());
|
||||
setConsoleReference({ node, position });
|
||||
};
|
||||
|
||||
@@ -109,7 +109,7 @@ const getNestedPropertiesCallback = (messageBus: MessageBus<Events>) => (
|
||||
propPath: string[]
|
||||
) => {
|
||||
const emitEmpty = () => messageBus.emit('nestedProperties', [position, { props: {} }, propPath]);
|
||||
const node = queryDirectiveForest(position.element, initializeOrGetDirectiveForestObserver().getDirectiveForest());
|
||||
const node = queryDirectiveForest(position.element, initializeOrGetDirectiveForestHooks().getDirectiveForest());
|
||||
if (!node) {
|
||||
return emitEmpty();
|
||||
}
|
||||
@@ -138,7 +138,7 @@ const checkForAngular = (messageBus: MessageBus<Events>, attempt = 0): void => {
|
||||
return;
|
||||
}
|
||||
if (appIsIvy) {
|
||||
initializeOrGetDirectiveForestObserver();
|
||||
initializeOrGetDirectiveForestHooks();
|
||||
}
|
||||
messageBus.emit('ngAvailability', [
|
||||
{ version: ngVersion.toString(), devMode: appIsAngularInDevMode(), ivy: appIsIvy },
|
||||
@@ -192,12 +192,12 @@ export const prepareForestForSerialization = (roots: ComponentTreeNode[]): Seria
|
||||
? {
|
||||
name: node.component.name,
|
||||
isElement: node.component.isElement,
|
||||
id: initializeOrGetDirectiveForestObserver().getDirectiveId(node.component.instance),
|
||||
id: initializeOrGetDirectiveForestHooks().getDirectiveId(node.component.instance),
|
||||
}
|
||||
: null,
|
||||
directives: node.directives.map((d) => ({
|
||||
name: d.name,
|
||||
id: initializeOrGetDirectiveForestObserver().getDirectiveId(d.instance),
|
||||
id: initializeOrGetDirectiveForestHooks().getDirectiveId(d.instance),
|
||||
})),
|
||||
children: prepareForestForSerialization(node.children),
|
||||
} as SerializableComponentTreeNode;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { unHighlight, highlight, findComponentAndHost } from '../highlighter';
|
||||
import { Type } from '@angular/core';
|
||||
import { buildDirectiveForest, ComponentTreeNode, findNodeInForest } from '../component-tree';
|
||||
import { ElementPosition } from 'protocol';
|
||||
import { initializeOrGetDirectiveForestObserver } from '../observer';
|
||||
import { initializeOrGetDirectiveForestHooks } from '../hooks';
|
||||
|
||||
export interface ComponentInspectorOptions {
|
||||
onComponentEnter: (id: number) => void;
|
||||
@@ -46,9 +46,7 @@ export class ComponentInspector {
|
||||
e.preventDefault();
|
||||
|
||||
if (this._selectedComponent.component && this._selectedComponent.host) {
|
||||
this._onComponentSelect(
|
||||
initializeOrGetDirectiveForestObserver().getDirectiveId(this._selectedComponent.component)
|
||||
);
|
||||
this._onComponentSelect(initializeOrGetDirectiveForestHooks().getDirectiveId(this._selectedComponent.component));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,9 +61,7 @@ export class ComponentInspector {
|
||||
unHighlight();
|
||||
if (this._selectedComponent.component && this._selectedComponent.host) {
|
||||
highlight(this._selectedComponent.host);
|
||||
this._onComponentEnter(
|
||||
initializeOrGetDirectiveForestObserver().getDirectiveId(this._selectedComponent.component)
|
||||
);
|
||||
this._onComponentEnter(initializeOrGetDirectiveForestHooks().getDirectiveId(this._selectedComponent.component));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-17
@@ -1,15 +1,15 @@
|
||||
import { DirectiveForestObserver, Hooks } from './observer';
|
||||
import { DirectiveForestHooks, Hooks } from './hooks';
|
||||
import { ElementPosition, ProfilerFrame, ElementProfile, DirectiveProfile, LifecycleProfile } from 'protocol';
|
||||
import { runOutsideAngular, isCustomElement } from '../utils';
|
||||
import { getDirectiveName } from '../highlighter';
|
||||
import { ComponentTreeNode } from '../component-tree';
|
||||
import { initializeOrGetDirectiveForestObserver } from '.';
|
||||
import { initializeOrGetDirectiveForestHooks } from '.';
|
||||
|
||||
let inProgress = false;
|
||||
let inChangeDetection = false;
|
||||
let eventMap: Map<any, DirectiveProfile>;
|
||||
let frameDuration = 0;
|
||||
let observerHooks: Partial<Hooks> = {};
|
||||
let hooks: Partial<Hooks> = {};
|
||||
|
||||
export const start = (onFrame: (frame: ProfilerFrame) => void): void => {
|
||||
if (inProgress) {
|
||||
@@ -17,15 +17,15 @@ export const start = (onFrame: (frame: ProfilerFrame) => void): void => {
|
||||
}
|
||||
eventMap = new Map<any, DirectiveProfile>();
|
||||
inProgress = true;
|
||||
observerHooks = getObserverHooks(onFrame);
|
||||
initializeOrGetDirectiveForestObserver().subscribe(observerHooks);
|
||||
hooks = getHooks(onFrame);
|
||||
initializeOrGetDirectiveForestHooks().subscribe(hooks);
|
||||
};
|
||||
|
||||
export const stop = (): ProfilerFrame => {
|
||||
const observer = initializeOrGetDirectiveForestObserver();
|
||||
const result = flushBuffer(observer);
|
||||
initializeOrGetDirectiveForestObserver().unsubscribe(observerHooks);
|
||||
observerHooks = {};
|
||||
const directiveForestHooks = initializeOrGetDirectiveForestHooks();
|
||||
const result = flushBuffer(directiveForestHooks);
|
||||
initializeOrGetDirectiveForestHooks().unsubscribe(hooks);
|
||||
hooks = {};
|
||||
inProgress = false;
|
||||
return result;
|
||||
};
|
||||
@@ -42,7 +42,7 @@ const getEventStart = (map: { [key: string]: number }, directive: any, label: st
|
||||
return map[key];
|
||||
};
|
||||
|
||||
const getObserverHooks = (onFrame: (frame: ProfilerFrame) => void) => {
|
||||
const getHooks = (onFrame: (frame: ProfilerFrame) => void) => {
|
||||
const timeStartMap: { [key: string]: number } = {};
|
||||
return {
|
||||
// We flush here because it's possible the current node to overwrite
|
||||
@@ -63,7 +63,7 @@ const getObserverHooks = (onFrame: (frame: ProfilerFrame) => void) => {
|
||||
runOutsideAngular(() => {
|
||||
Promise.resolve().then(() => {
|
||||
inChangeDetection = false;
|
||||
onFrame(flushBuffer(initializeOrGetDirectiveForestObserver(), source));
|
||||
onFrame(flushBuffer(initializeOrGetDirectiveForestHooks(), source));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -196,14 +196,14 @@ const prepareInitialFrame = (source: string, duration: number) => {
|
||||
duration,
|
||||
directives: [],
|
||||
};
|
||||
const observer = initializeOrGetDirectiveForestObserver();
|
||||
const directiveForest = observer.getDirectiveForest();
|
||||
const directiveForestHooks = initializeOrGetDirectiveForestHooks();
|
||||
const directiveForest = directiveForestHooks.getDirectiveForest();
|
||||
const traverse = (node: ComponentTreeNode, children = frame.directives) => {
|
||||
let position: ElementPosition | undefined;
|
||||
if (node.component) {
|
||||
position = observer.getDirectivePosition(node.component.instance);
|
||||
position = directiveForestHooks.getDirectivePosition(node.component.instance);
|
||||
} else {
|
||||
position = observer.getDirectivePosition(node.directives[0].instance);
|
||||
position = directiveForestHooks.getDirectivePosition(node.directives[0].instance);
|
||||
}
|
||||
if (position === undefined) {
|
||||
return;
|
||||
@@ -235,12 +235,12 @@ const prepareInitialFrame = (source: string, duration: number) => {
|
||||
return frame;
|
||||
};
|
||||
|
||||
const flushBuffer = (obs: DirectiveForestObserver, source: string = '') => {
|
||||
const flushBuffer = (directiveForestHooks: DirectiveForestHooks, source: string = '') => {
|
||||
const items = Array.from(eventMap.keys());
|
||||
const positions: ElementPosition[] = [];
|
||||
const positionDirective = new Map<ElementPosition, any>();
|
||||
items.forEach((dir) => {
|
||||
const position = obs.getDirectivePosition(dir);
|
||||
const position = directiveForestHooks.getDirectivePosition(dir);
|
||||
if (position === undefined) {
|
||||
return;
|
||||
}
|
||||
+3
-13
@@ -102,15 +102,10 @@ const getLifeCycleName = (obj: {}, fn: any): keyof LifecycleProfile | 'unknown'
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a temporal "polyfill" until we receive more comprehensive framework
|
||||
* debugging APIs. This observer checks for new elements added. When it detects
|
||||
* this has happened, it checks if any of the elements in the tree with root
|
||||
* the added element is a component. If it is, it throws a creation event.
|
||||
* The polyfill also patches the tView template function reference to allow
|
||||
* tracking of how much time we spend in the particular component in change detection.
|
||||
* This is a temporal "polyfill" until we receive
|
||||
* more comprehensive framework debugging APIs.
|
||||
*/
|
||||
export class DirectiveForestObserver {
|
||||
private _mutationObserver = new MutationObserver(this._onMutation.bind(this));
|
||||
export class DirectiveForestHooks {
|
||||
private _patched = new Map<any, () => void>();
|
||||
private _undoLifecyclePatch: (() => void)[] = [];
|
||||
private _lastChangeDetection = new Map<any, number>();
|
||||
@@ -150,15 +145,10 @@ export class DirectiveForestObserver {
|
||||
}
|
||||
|
||||
initialize(): void {
|
||||
this._mutationObserver.observe(document, {
|
||||
subtree: true,
|
||||
childList: true,
|
||||
});
|
||||
this.indexForest();
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this._mutationObserver.disconnect();
|
||||
this._lastChangeDetection = new Map<any, number>();
|
||||
this._tracker.destroy();
|
||||
|
||||
+8
-8
@@ -1,5 +1,5 @@
|
||||
import { getDirectiveName } from '../highlighter';
|
||||
import { DirectiveForestObserver } from './observer';
|
||||
import { DirectiveForestHooks } from './hooks';
|
||||
import { LifecycleProfile } from 'protocol';
|
||||
|
||||
const markName = (s: string, method: Method) => `🅰️ ${s}#${method}`;
|
||||
@@ -36,12 +36,12 @@ export const disableTimingAPI = () => (timingAPIFlag = false);
|
||||
|
||||
const timingAPIEnabled = () => timingAPIFlag;
|
||||
|
||||
export let observer: DirectiveForestObserver;
|
||||
export const initializeOrGetDirectiveForestObserver = () => {
|
||||
if (observer) {
|
||||
return observer;
|
||||
export let directiveForestHooks: DirectiveForestHooks;
|
||||
export const initializeOrGetDirectiveForestHooks = () => {
|
||||
if (directiveForestHooks) {
|
||||
return directiveForestHooks;
|
||||
}
|
||||
observer = new DirectiveForestObserver({
|
||||
directiveForestHooks = new DirectiveForestHooks({
|
||||
onChangeDetectionStart(component: any): void {
|
||||
if (!timingAPIEnabled()) {
|
||||
return;
|
||||
@@ -67,6 +67,6 @@ export const initializeOrGetDirectiveForestObserver = () => {
|
||||
endMark(getDirectiveName(component), lifecyle);
|
||||
},
|
||||
});
|
||||
observer.initialize();
|
||||
return observer;
|
||||
directiveForestHooks.initialize();
|
||||
return directiveForestHooks;
|
||||
};
|
||||
Reference in New Issue
Block a user