perf(devtools): prevent directive forest from building twice every time a node is selected in the directive explorer

This commit is contained in:
AleksanderBodurri 2020-11-14 17:06:04 -05:00 committed by Minko Gechev
parent 1a00837b0c
commit ca0910e59c
5 changed files with 40 additions and 15 deletions

View file

@ -71,19 +71,21 @@ 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.
initializeOrGetDirectiveForestHooks().indexForest();
if (!query) {
messageBus.emit('latestComponentExplorerView', [
{
forest: prepareForestForSerialization(initializeOrGetDirectiveForestHooks().getDirectiveForest()),
forest: prepareForestForSerialization(initializeOrGetDirectiveForestHooks().getIndexedDirectiveForest()),
},
]);
return;
}
messageBus.emit('latestComponentExplorerView', [
{
forest: prepareForestForSerialization(initializeOrGetDirectiveForestHooks().getDirectiveForest()),
properties: getLatestComponentState(query),
forest: prepareForestForSerialization(initializeOrGetDirectiveForestHooks().getIndexedDirectiveForest()),
properties: getLatestComponentState(query, initializeOrGetDirectiveForestHooks().getDirectiveForest()),
},
]);
};
@ -100,7 +102,7 @@ const stopProfilingCallback = (messageBus: MessageBus<Events>) => () => {
};
const selectedComponentCallback = (position: ElementPosition) => {
const node = queryDirectiveForest(position, initializeOrGetDirectiveForestHooks().getDirectiveForest());
const node = queryDirectiveForest(position, initializeOrGetDirectiveForestHooks().getIndexedDirectiveForest());
setConsoleReference({ node, position });
};
@ -109,7 +111,10 @@ const getNestedPropertiesCallback = (messageBus: MessageBus<Events>) => (
propPath: string[]
) => {
const emitEmpty = () => messageBus.emit('nestedProperties', [position, { props: {} }, propPath]);
const node = queryDirectiveForest(position.element, initializeOrGetDirectiveForestHooks().getDirectiveForest());
const node = queryDirectiveForest(
position.element,
initializeOrGetDirectiveForestHooks().getIndexedDirectiveForest()
);
if (!node) {
return emitEmpty();
}

View file

@ -28,8 +28,14 @@ export interface ComponentTreeNode extends DevToolsNode<DirectiveInstanceType, C
children: ComponentTreeNode[];
}
export const getLatestComponentState = (query: ComponentExplorerViewQuery): DirectivesProperties | undefined => {
const node = queryDirectiveForest(query.selectedElement, buildDirectiveForest());
export const getLatestComponentState = (
query: ComponentExplorerViewQuery,
directiveForest?: ComponentTreeNode[]
): DirectivesProperties | undefined => {
// if a directive forest is passed in we don't have to build the forest again.
directiveForest = directiveForest ?? buildDirectiveForest();
const node = queryDirectiveForest(query.selectedElement, directiveForest);
if (!node) {
return;
}

View file

@ -197,7 +197,7 @@ const prepareInitialFrame = (source: string, duration: number) => {
directives: [],
};
const directiveForestHooks = initializeOrGetDirectiveForestHooks();
const directiveForest = directiveForestHooks.getDirectiveForest();
const directiveForest = directiveForestHooks.getIndexedDirectiveForest();
const traverse = (node: ComponentTreeNode, children = frame.directives) => {
let position: ElementPosition | undefined;
if (node.component) {

View file

@ -1,3 +1,4 @@
import { ComponentTreeNode } from './../component-tree';
import { ElementPosition, LifecycleProfile } from 'protocol';
import { componentMetadata, runOutsideAngular } from '../utils';
import { IdentityTracker, IndexedNode } from './identity-tracker';
@ -109,7 +110,8 @@ export class DirectiveForestHooks {
private _undoLifecyclePatch: (() => void)[] = [];
private _lastChangeDetection = new Map<any, number>();
private _tracker = new IdentityTracker();
private _forest: IndexedNode[] = [];
private _forest: ComponentTreeNode[] = [];
private _indexedForest: IndexedNode[] = [];
private _inChangeDetection = false;
private _changeDetection$ = new Subject<void>();
@ -139,7 +141,11 @@ export class DirectiveForestHooks {
return result;
}
getDirectiveForest(): IndexedNode[] {
getIndexedDirectiveForest(): IndexedNode[] {
return this._indexedForest;
}
getDirectiveForest(): ComponentTreeNode[] {
return this._forest;
}
@ -163,8 +169,9 @@ export class DirectiveForestHooks {
}
indexForest(): void {
const { newNodes, removedNodes, indexedForest } = this._tracker.index();
this._forest = indexedForest;
const { newNodes, removedNodes, indexedForest, directiveForest } = this._tracker.index();
this._indexedForest = indexedForest;
this._forest = directiveForest;
newNodes.forEach((node) => {
this._observeLifecycle(node.directive, node.isComponent);
this._observeComponent(node.directive);

View file

@ -1,3 +1,4 @@
import { ComponentTreeNode } from './../component-tree';
import { ElementPosition, DevToolsNode } from 'protocol';
import { buildDirectiveForest, DirectiveInstanceType, ComponentInstanceType } from '../component-tree';
import { Type } from '@angular/core';
@ -31,8 +32,14 @@ export class IdentityTracker {
return this._currentDirectiveId.has(dir);
}
index(): { newNodes: NodeArray; removedNodes: NodeArray; indexedForest: IndexedNode[] } {
const indexedForest = indexForest(buildDirectiveForest());
index(): {
newNodes: NodeArray;
removedNodes: NodeArray;
indexedForest: IndexedNode[];
directiveForest: ComponentTreeNode[];
} {
const directiveForest = buildDirectiveForest();
const indexedForest = indexForest(directiveForest);
const newNodes: NodeArray = [];
const removedNodes: NodeArray = [];
const allNodes = new Set<any>();
@ -46,7 +53,7 @@ export class IdentityTracker {
// this._currentDirectivePosition.delete(dir);
}
});
return { newNodes, removedNodes, indexedForest };
return { newNodes, removedNodes, indexedForest, directiveForest };
}
private _index(