refactor(ivy): simplify differentiation of LView, RNode, LView, LContainer, StylingContext (#28947)

For efficiency reasons we often put several different data types (`RNode`, `LView`, `LContainer`,
`StylingContext`) in same location in `LView`. This is because we don't want to pre-allocate
space
for it because the storage is sparse. This file contains utilities for dealing with such data
types.
How do we know what is stored at a given location in `LView`.
- `Array.isArray(value) === false` => `RNode` (The normal storage value)
- `Array.isArray(value) === true` => than the `value[0]` represents the wrapped value.
  - `typeof value[TYPE] === 'object'` => `LView`
     - This happens when we have a component at a given location
  - `typeof value[TYPE] === 'number'` => `StylingContext`
     - This happens when we have style/class binding at a given location.
  - `typeof value[TYPE] === true` => `LContainer`
     - This happens when we have `LContainer` binding at a given location.
NOTE: it is assumed that `Array.isArray` and `typeof` operations are very efficient.

PR Close #28947
This commit is contained in:
Misko Hevery
2019-02-23 11:14:35 -08:00
committed by Miško Hevery
parent bd65f58784
commit 3cb497c6ac
19 changed files with 225 additions and 112 deletions

View File

@ -11,25 +11,22 @@ import {RComment, RElement} from './renderer';
import {StylingContext} from './styling';
import {HOST, LView, NEXT, PARENT, QUERIES} from './view';
/**
* Special location which allows easy identification of type. If we have an array which was
* retrieved from the `LView` and that array has `true` at `TYPE` location, we know it is
* `LContainer`.
*/
export const TYPE = 1;
/**
* Below are constants for LContainer indices to help us look up LContainer members
* without having to remember the specific indices.
* Uglify will inline these when minifying so there shouldn't be a cost.
*/
export const ACTIVE_INDEX = 1;
export const VIEWS = 2;
// PARENT, NEXT, QUERIES, and HOST are indices 2, 3, 4, and 5.
export const ACTIVE_INDEX = 2;
// PARENT, NEXT, and QUERIES are indices 3, 4, and 5.
// As we already have these constants in LView, we don't need to re-create them.
export const NATIVE = 6;
// Because interfaces in TS/JS cannot be instanceof-checked this means that we
// need to rely on predictable characteristics of data-structures to check if they
// are what we expect for them to be. The `LContainer` interface code below has a
// fixed length and the constant value below references that. Using the length value
// below we can predictably gaurantee that we are dealing with an `LContainer` array.
// This value MUST be kept up to date with the length of the `LContainer` array
// interface below so that runtime type checking can work.
export const LCONTAINER_LENGTH = 7;
export const VIEWS = 6;
export const NATIVE = 7;
/**
* The state associated with a container.
@ -51,6 +48,12 @@ export interface LContainer extends Array<any> {
*/
readonly[HOST]: RElement|RComment|StylingContext|LView;
/**
* This is a type field which allows us to differentiate `LContainer` from `StylingContext` in an
* efficient way. The value is always set to `true`
*/
[TYPE]: true;
/**
* The next active index in the views array to read or write to. This helps us
* keep track of where we are in the views array.
@ -60,15 +63,6 @@ export interface LContainer extends Array<any> {
*/
[ACTIVE_INDEX]: number;
/**
* A list of the container's currently active child views. Views will be inserted
* here as they are added and spliced from here when they are removed. We need
* to keep a record of current views so we know which views are already in the DOM
* (and don't need to be re-added) and so we can remove views from the DOM when they
* are no longer required.
*/
[VIEWS]: LView[];
/**
* Access to the parent view is necessary so we can propagate back
* up from inside a container to parent[NEXT].
@ -85,10 +79,21 @@ export interface LContainer extends Array<any> {
* Queries active for this container - all the views inserted to / removed from
* this container are reported to queries referenced here.
*/
[QUERIES]: LQueries|null;
[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)
/**
* A list of the container's currently active child views. Views will be inserted
* here as they are added and spliced from here when they are removed. We need
* to keep a record of current views so we know which views are already in the DOM
* (and don't need to be re-added) and so we can remove views from the DOM when they
* are no longer required.
*/
[VIEWS]: LView[];
/** The comment element that serves as an anchor for this LContainer. */
readonly[NATIVE]: RComment;
readonly[NATIVE]:
RComment; // TODO(misko): remove as this value can be gotten by unwrapping `[HOST]`
}
// Note: This hack is necessary so we don't erroneously get a circular dependency

View File

@ -7,7 +7,7 @@
*/
import {RElement} from './renderer';
import {RNode} from './renderer';
import {LView} from './view';
/**
@ -39,7 +39,7 @@ export interface LContext {
/**
* The instance of the DOM node that is attached to the lNode.
*/
native: RElement;
native: RNode;
/**
* The instance of the Component node.

View File

@ -7,7 +7,10 @@
*/
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
import {RElement} from '../interfaces/renderer';
import {LContainer} from './container';
import {PlayerContext} from './player';
import {LView} from './view';
/**
* The styling context acts as a styling manifest (shaped as an array) for determining which
@ -263,7 +266,7 @@ export interface StylingContext extends
/**
* Location of element that is used as a target for this context.
*/
[StylingIndex.ElementPosition]: RElement|null;
[StylingIndex.ElementPosition]: LContainer|LView|RElement|null;
/**
* A numeric value representing the configuration status (whether the context is dirty or not)

View File

@ -72,6 +72,9 @@ export interface LView extends Array<any> {
* The host node for this LView instance, if this is a component view.
*
* If this is an embedded view, HOST will be null.
*
* If the component uses host bindings for styling that the `RElement` will be wrapped with
* `StylingContext`.
*/
[HOST]: RElement|StylingContext|null;