perf(ivy): match query results on the TView level (#31489)
PR Close #31489
This commit is contained in:

committed by
Kara Erickson

parent
9c954ebc62
commit
d52ae7cbab
@ -9,9 +9,9 @@
|
||||
import {ViewRef} from '../../linker/view_ref';
|
||||
|
||||
import {TNode} from './node';
|
||||
import {LQueries} from './query';
|
||||
import {RComment, RElement} from './renderer';
|
||||
import {HOST, LView, NEXT, PARENT, QUERIES, T_HOST} from './view';
|
||||
|
||||
import {HOST, LView, NEXT, PARENT, T_HOST} from './view';
|
||||
|
||||
|
||||
/**
|
||||
@ -26,8 +26,15 @@ export const TYPE = 1;
|
||||
* Uglify will inline these when minifying so there shouldn't be a cost.
|
||||
*/
|
||||
export const ACTIVE_INDEX = 2;
|
||||
// PARENT, NEXT, QUERIES and T_HOST are indices 3, 4, 5 and 6.
|
||||
|
||||
// PARENT and NEXT are indices 3 and 4
|
||||
// As we already have these constants in LView, we don't need to re-create them.
|
||||
|
||||
export const MOVED_VIEWS = 5;
|
||||
|
||||
// T_HOST is index 6
|
||||
// We already have this constants in LView, we don't need to re-create it.
|
||||
|
||||
export const NATIVE = 7;
|
||||
export const VIEW_REFS = 8;
|
||||
|
||||
@ -84,11 +91,11 @@ export interface LContainer extends Array<any> {
|
||||
[NEXT]: LView|LContainer|null;
|
||||
|
||||
/**
|
||||
* Queries active for this container - all the views inserted to / removed from
|
||||
* this container are reported to queries referenced here.
|
||||
* A collection of views created based on the underlying `<ng-template>` element but inserted into
|
||||
* a different `LContainer`. We need to track views created from a given declaration point since
|
||||
* queries collect matches from the embedded view declaration point and _not_ the insertion point.
|
||||
*/
|
||||
[QUERIES]: LQueries|null; // TODO(misko): This is abuse of `LContainer` since we are storing
|
||||
// `[QUERIES]` in it which are not needed for `LContainer` (only needed for Template)
|
||||
[MOVED_VIEWS]: LView[]|null;
|
||||
|
||||
/**
|
||||
* Pointer to the `TNode` which represents the host of the container.
|
||||
|
@ -9,99 +9,211 @@
|
||||
import {Type} from '../../interface/type';
|
||||
import {QueryList} from '../../linker';
|
||||
|
||||
import {TContainerNode, TElementContainerNode, TElementNode, TNode} from './node';
|
||||
import {TNode} from './node';
|
||||
import {TView} from './view';
|
||||
|
||||
/**
|
||||
* An object representing query metadata extracted from query annotations.
|
||||
*/
|
||||
export interface TQueryMetadata {
|
||||
predicate: Type<any>|string[];
|
||||
descendants: boolean;
|
||||
read: any;
|
||||
isStatic: boolean;
|
||||
}
|
||||
|
||||
/** Used for tracking queries (e.g. ViewChild, ContentChild). */
|
||||
/**
|
||||
* TQuery objects represent all the query-related data that remain the same from one view instance
|
||||
* to another and can be determined on the very first template pass. Most notably TQuery holds all
|
||||
* the matches for a given view.
|
||||
*/
|
||||
export interface TQuery {
|
||||
/**
|
||||
* Query metadata extracted from query annotations.
|
||||
*/
|
||||
metadata: TQueryMetadata;
|
||||
|
||||
/**
|
||||
* Index of a query in a declaration view in case of queries propagated to en embedded view, -1
|
||||
* for queries declared in a given view. We are storing this index so we can find a parent query
|
||||
* to clone for an embedded view (when an embedded view is created).
|
||||
*/
|
||||
indexInDeclarationView: number;
|
||||
|
||||
/**
|
||||
* Matches collected on the first template pass. Each match is a pair of:
|
||||
* - TNode index;
|
||||
* - match index;
|
||||
*
|
||||
* A TNode index can be either:
|
||||
* - a positive number (the most common case) to indicate a matching TNode;
|
||||
* - a negative number to indicate that a given query is crossing a <ng-template> element and
|
||||
* results from views created based on TemplateRef should be inserted at this place.
|
||||
*
|
||||
* A match index is a number used to find an actual value (for a given node) when query results
|
||||
* are materialized. This index can have one of the following values:
|
||||
* - -2 - indicates that we need to read a special token (TemplateRef, ViewContainerRef etc.);
|
||||
* - -1 - indicates that we need to read a default value based on the node type (TemplateRef for
|
||||
* ng-template and ElementRef for other elements);
|
||||
* - a positive number - index of an injectable to be read from the element injector.
|
||||
*/
|
||||
matches: number[]|null;
|
||||
|
||||
/**
|
||||
* A flag indicating if a given query crosses an <ng-template> element. This flag exists for
|
||||
* performance reasons: we can notice that queries not crossing any <ng-template> elements will
|
||||
* have matches from a given view only (and adapt processing accordingly).
|
||||
*/
|
||||
crossesNgTemplate: boolean;
|
||||
|
||||
/**
|
||||
* A method call when a given query is crossing an element (or element container). This is where a
|
||||
* given TNode is matched against a query predicate.
|
||||
* @param tView
|
||||
* @param tNode
|
||||
*/
|
||||
elementStart(tView: TView, tNode: TNode): void;
|
||||
|
||||
/**
|
||||
* A method called when processing the elementEnd instruction - this is mostly useful to determine
|
||||
* if a given content query should match any nodes past this point.
|
||||
* @param tNode
|
||||
*/
|
||||
elementEnd(tNode: TNode): void;
|
||||
|
||||
/**
|
||||
* A method called when processing the template instruction. This is where a
|
||||
* given TContainerNode is matched against a query predicate.
|
||||
* @param tView
|
||||
* @param tNode
|
||||
*/
|
||||
template(tView: TView, tNode: TNode): void;
|
||||
|
||||
/**
|
||||
* A query-related method called when an embedded TView is created based on the content of a
|
||||
* <ng-template> element. We call this method to determine if a given query should be propagated
|
||||
* to the embedded view and if so - return a cloned TQuery for this embedded view.
|
||||
* @param tNode
|
||||
* @param childQueryIndex
|
||||
*/
|
||||
embeddedTView(tNode: TNode, childQueryIndex: number): TQuery|null;
|
||||
}
|
||||
|
||||
/**
|
||||
* TQueries represent a collection of individual TQuery objects tracked in a given view. Most of the
|
||||
* methods on this interface are simple proxy methods to the corresponding functionality on TQuery.
|
||||
*/
|
||||
export interface TQueries {
|
||||
/**
|
||||
* Adds a new TQuery to a collection of queries tracked in a given view.
|
||||
* @param tQuery
|
||||
*/
|
||||
track(tQuery: TQuery): void;
|
||||
|
||||
/**
|
||||
* Returns a TQuery instance for at the given index in the queries array.
|
||||
* @param index
|
||||
*/
|
||||
getByIndex(index: number): TQuery;
|
||||
|
||||
/**
|
||||
* Returns the number of queries tracked in a given view.
|
||||
*/
|
||||
length: number;
|
||||
|
||||
/**
|
||||
* A proxy method that iterates over all the TQueries in a given TView and calls the corresponding
|
||||
* `elementStart` on each and every TQuery.
|
||||
* @param tView
|
||||
* @param tNode
|
||||
*/
|
||||
elementStart(tView: TView, tNode: TNode): void;
|
||||
|
||||
/**
|
||||
* A proxy method that iterates over all the TQueries in a given TView and calls the corresponding
|
||||
* `elementEnd` on each and every TQuery.
|
||||
* @param tNode
|
||||
*/
|
||||
elementEnd(tNode: TNode): void;
|
||||
|
||||
/**
|
||||
* A proxy method that iterates over all the TQueries in a given TView and calls the corresponding
|
||||
* `template` on each and every TQuery.
|
||||
* @param tView
|
||||
* @param tNode
|
||||
*/
|
||||
template(tView: TView, tNode: TNode): void;
|
||||
|
||||
/**
|
||||
* A proxy method that iterates over all the TQueries in a given TView and calls the corresponding
|
||||
* `embeddedTView` on each and every TQuery.
|
||||
* @param tNode
|
||||
*/
|
||||
embeddedTView(tNode: TNode): TQueries|null;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface that represents query-related information specific to a view instance. Most notably
|
||||
* it contains:
|
||||
* - materialized query matches;
|
||||
* - a pointer to a QueryList where materialized query results should be reported.
|
||||
*/
|
||||
export interface LQuery<T> {
|
||||
/**
|
||||
* Materialized query matches for a given view only (!). Results are initialized lazily so the
|
||||
* array of matches is set to `null` initially.
|
||||
*/
|
||||
matches: (T|null)[]|null;
|
||||
|
||||
/**
|
||||
* A QueryList where materialized query results should be reported.
|
||||
*/
|
||||
queryList: QueryList<T>;
|
||||
|
||||
/**
|
||||
* Clones an LQuery for an embedded view. A cloned query shares the same `QueryList` but has a
|
||||
* separate collection of materialized matches.
|
||||
*/
|
||||
clone(): LQuery<T>;
|
||||
|
||||
/**
|
||||
* Called when an embedded view, impacting results of this query, is inserted or removed.
|
||||
*/
|
||||
setDirty(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* lQueries represent a collection of individual LQuery objects tracked in a given view.
|
||||
*/
|
||||
export interface LQueries {
|
||||
/**
|
||||
* The parent LQueries instance.
|
||||
*
|
||||
* When there is a content query, a new LQueries instance is created to avoid mutating any
|
||||
* existing LQueries. After we are done searching content children, the parent property allows
|
||||
* us to traverse back up to the original LQueries instance to continue to search for matches
|
||||
* in the main view.
|
||||
* A collection of queries tracked in a given view.
|
||||
*/
|
||||
parent: LQueries|null;
|
||||
queries: LQuery<any>[];
|
||||
|
||||
/**
|
||||
* The index of the node on which this LQueries instance was created / cloned in a given LView.
|
||||
*
|
||||
* This index is stored to minimize LQueries cloning: we can observe that LQueries can be mutated
|
||||
* only under 2 conditions:
|
||||
* - we are crossing an element that has directives with content queries (new queries are added);
|
||||
* - we are descending into element hierarchy (creating a child element of an existing element)
|
||||
* and the current LQueries object is tracking shallow queries (shallow queries are removed).
|
||||
*
|
||||
* Since LQueries are not cloned systematically we need to know exactly where (on each element)
|
||||
* cloning occurred, so we can properly restore the set of tracked queries when going up the
|
||||
* elements hierarchy.
|
||||
*
|
||||
* Always set to -1 for view queries as view queries are created before we process any node in a
|
||||
* given view.
|
||||
* A method called when a new embedded view is created. As a result a set of LQueries applicable
|
||||
* for a new embedded view is instantiated (cloned) from the declaration view.
|
||||
* @param tView
|
||||
*/
|
||||
nodeIndex: number;
|
||||
createEmbeddedView(tView: TView): LQueries|null;
|
||||
|
||||
/**
|
||||
* Ask queries to prepare a copy of itself. This ensures that:
|
||||
* - tracking new queries on content nodes doesn't mutate list of queries tracked on a parent
|
||||
* node;
|
||||
* - we don't track shallow queries when descending into elements hierarchy.
|
||||
*
|
||||
* We will clone LQueries before constructing content queries
|
||||
* A method called when an embedded view is inserted into a container. As a result all impacted
|
||||
* `LQuery` objects (and associated `QueryList`) are marked as dirty.
|
||||
* @param tView
|
||||
*/
|
||||
clone(tNode: TNode): LQueries;
|
||||
insertView(tView: TView): void;
|
||||
|
||||
/**
|
||||
* Notify `LQueries` that a new `TNode` has been created and needs to be added to query results
|
||||
* if matching query predicate.
|
||||
* A method called when an embedded view is detached from a container. As a result all impacted
|
||||
* `LQuery` objects (and associated `QueryList`) are marked as dirty.
|
||||
* @param tView
|
||||
*/
|
||||
addNode(tNode: TElementNode|TContainerNode|TElementContainerNode): void;
|
||||
|
||||
/**
|
||||
* Notify `LQueries` that a new `TNode` has been created and needs to be added to query results
|
||||
* if matching query predicate. This is a special mode invoked if the query container has to
|
||||
* be created out of order (e.g. view created in the constructor of a directive).
|
||||
*/
|
||||
insertNodeBeforeViews(tNode: TElementNode|TContainerNode|TElementContainerNode): void;
|
||||
|
||||
/**
|
||||
* Notify `LQueries` that a new LContainer was added to ivy data structures. As a result we need
|
||||
* to prepare room for views that might be inserted into this container.
|
||||
*/
|
||||
container(): LQueries|null;
|
||||
|
||||
/**
|
||||
* Notify `LQueries` that a new `LView` has been created. As a result we need to prepare room
|
||||
* and collect nodes that match query predicate.
|
||||
*/
|
||||
createView(): LQueries|null;
|
||||
|
||||
/**
|
||||
* Notify `LQueries` that a new `LView` has been added to `LContainer`. As a result all
|
||||
* the matching nodes from this view should be added to container's queries.
|
||||
*/
|
||||
insertView(newViewIndex: number): void;
|
||||
|
||||
/**
|
||||
* Notify `LQueries` that an `LView` has been removed from `LContainer`. As a result all
|
||||
* the matching nodes from this view should be removed from container's queries.
|
||||
*/
|
||||
removeView(): void;
|
||||
|
||||
/**
|
||||
* Add additional `QueryList` to track.
|
||||
*
|
||||
* @param queryList `QueryList` to update with changes.
|
||||
* @param predicate Either `Type` or selector array of [key, value] predicates.
|
||||
* @param descend If true the query will recursively apply to the children.
|
||||
* @param read Indicates which token should be read from DI for this query.
|
||||
*/
|
||||
track<T>(
|
||||
queryList: QueryList<T>, predicate: Type<any>|string[], descend?: boolean,
|
||||
read?: Type<T>): void;
|
||||
detachView(tView: TView): void;
|
||||
}
|
||||
|
||||
|
||||
// Note: This hack is necessary so we don't erroneously get a circular dependency
|
||||
// failure based on types.
|
||||
export const unusedValueExportToPlacateAjd = 1;
|
||||
|
@ -9,7 +9,6 @@
|
||||
import {InjectionToken} from '../../di/injection_token';
|
||||
import {Injector} from '../../di/injector';
|
||||
import {Type} from '../../interface/type';
|
||||
import {QueryList} from '../../linker';
|
||||
import {SchemaMetadata} from '../../metadata';
|
||||
import {Sanitizer} from '../../sanitization/security';
|
||||
|
||||
@ -18,7 +17,7 @@ import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefList, HostBin
|
||||
import {I18nUpdateOpCodes, TI18n} from './i18n';
|
||||
import {TElementNode, TNode, TViewNode} from './node';
|
||||
import {PlayerHandler} from './player';
|
||||
import {LQueries} from './query';
|
||||
import {LQueries, TQueries} from './query';
|
||||
import {RElement, Renderer3, RendererFactory3} from './renderer';
|
||||
|
||||
|
||||
@ -42,11 +41,11 @@ export const RENDERER = 12;
|
||||
export const SANITIZER = 13;
|
||||
export const CHILD_HEAD = 14;
|
||||
export const CHILD_TAIL = 15;
|
||||
export const CONTENT_QUERIES = 16;
|
||||
export const DECLARATION_VIEW = 17;
|
||||
export const DECLARATION_VIEW = 16;
|
||||
export const DECLARATION_LCONTAINER = 17;
|
||||
export const PREORDER_HOOK_FLAGS = 18;
|
||||
/** Size of LView's header. Necessary to adjust for it when setting slots. */
|
||||
export const HEADER_OFFSET = 20;
|
||||
export const HEADER_OFFSET = 19;
|
||||
|
||||
|
||||
// This interface replaces the real LView interface if it is an arg or a
|
||||
@ -179,13 +178,6 @@ export interface LView extends Array<any> {
|
||||
*/
|
||||
[CHILD_TAIL]: LView|LContainer|null;
|
||||
|
||||
/**
|
||||
* Stores QueryLists associated with content queries of a directive. This data structure is
|
||||
* filled-in as part of a directive creation process and is later used to retrieve a QueryList to
|
||||
* be refreshed.
|
||||
*/
|
||||
[CONTENT_QUERIES]: QueryList<any>[]|null;
|
||||
|
||||
/**
|
||||
* View where this view's template was declared.
|
||||
*
|
||||
@ -212,6 +204,16 @@ export interface LView extends Array<any> {
|
||||
*/
|
||||
[DECLARATION_VIEW]: LView|null;
|
||||
|
||||
/**
|
||||
* A declaration point of embedded views (ones instantiated based on the content of a
|
||||
* <ng-template>), null for other types of views.
|
||||
*
|
||||
* We need to track all embedded views created from a given declaration point so we can prepare
|
||||
* query matches in a proper order (query matches are ordered based on their declaration point and
|
||||
* _not_ the insertion point).
|
||||
*/
|
||||
[DECLARATION_LCONTAINER]: LContainer|null;
|
||||
|
||||
/**
|
||||
* More flags for this view. See PreOrderHookFlags for more info.
|
||||
*/
|
||||
@ -410,17 +412,6 @@ export interface TView {
|
||||
*/
|
||||
staticContentQueries: boolean;
|
||||
|
||||
/**
|
||||
* The index where the viewQueries section of `LView` begins. This section contains
|
||||
* view queries defined for a component/directive.
|
||||
*
|
||||
* We store this start index so we know where the list of view queries starts.
|
||||
* This is required when we invoke view queries at runtime. We invoke queries one by one and
|
||||
* increment query index after each iteration. This information helps us to reset index back to
|
||||
* the beginning of view query list before we invoke view queries again.
|
||||
*/
|
||||
viewQueryStartIndex: number;
|
||||
|
||||
/**
|
||||
* A reference to the first child node located in the view.
|
||||
*/
|
||||
@ -550,7 +541,19 @@ export interface TView {
|
||||
components: number[]|null;
|
||||
|
||||
/**
|
||||
* A list of indices for child directives that have content queries.
|
||||
* A collection of queries tracked in a given view.
|
||||
*/
|
||||
queries: TQueries|null;
|
||||
|
||||
/**
|
||||
* An array of indices pointing to directives with content queries alongside with the
|
||||
* corresponding
|
||||
* query index. Each entry in this array is a tuple of:
|
||||
* - index of the first content query index declared by a given directive;
|
||||
* - index of a directive.
|
||||
*
|
||||
* We are storing those indexes so we can refresh content queries as part of a view refresh
|
||||
* process.
|
||||
*/
|
||||
contentQueries: number[]|null;
|
||||
|
||||
|
Reference in New Issue
Block a user