Skip to content

Instantly share code, notes, and snippets.

@studioTeaTwo
Created February 10, 2018 17:10
Show Gist options
  • Save studioTeaTwo/4db822cea60b21270b1e80d83e8ff464 to your computer and use it in GitHub Desktop.
Save studioTeaTwo/4db822cea60b21270b1e80d83e8ff464 to your computer and use it in GitHub Desktop.

Tree Shaking Principles

When designing code please keep these principles in mind

Enums for features are not-tree-shakable

Here is an example of code which is not tree shakable.

export function query<T>(
    predicate: Type<any>| string[], descend?: boolean,
    read?: QueryReadType | Type<T>): QueryList<T> {
  ngDevMode && assertPreviousIsParent();
  const queryList = new QueryList<T>();
  const query = currentQuery || (currentQuery = new LQuery_());
  query.track(queryList, predicate, descend, read);
  return queryList;
}

Notice that query() takes the QueryReadType as enumeration.

function readFromNodeInjector(
    nodeInjector: LInjector, node: LNode, read: QueryReadType | Type<any>): any {
  if (read === QueryReadType.ElementRef) {
    return getOrCreateElementRef(nodeInjector);
  } 
  if (read === QueryReadType.ViewContainerRef) {
    return getOrCreateContainerRef(nodeInjector);
  }
  if (read === QueryReadType.TemplateRef) {
    return getOrCreateTemplateRef(nodeInjector);
  } 
  const matchingIdx = geIdxOfMatchingDirective(node, read);
  if (matchingIdx !== null) {
    return node.view.data[matchingIdx];
  }
  return null;
}

Sometimes later in the above code the readFromNodeInjector takes the QueryReadType enumeration and performs specific behavior.

The issue is that once the query instruction is pulled in it will pull in ElementRef, ContainerRef, and TemplateRef regardless if the query instruction queries for them.

A better way to do this is to encapsulate the work into an object or function which will then be passed into the query instead of the enumeration.

function queryElementRefFeature() {...}
function queryContainerRefFeature() {...}
function queryTemplateRefFeature() {...}

query(predicate, descend, queryElementRefFeature) {...}

this would allow the readFromNodeInjector to simply call the read function (or object) like so.

function readFromNodeInjector(
    nodeInjector: LInjector, node: LNode, readFn: (injector: Injector) => any) | Type<any>): any {
  if (isFeature(readFn)) {
    return readFn(nodeInjector);
  } 
  const matchingIdx = geIdxOfMatchingDirective(node, readFn);
  if (matchingIdx !== null) {
    return node.view.data[matchingIdx];
  }
  return null;
}

This approach allows us to preserve the tree-shaking. In essence the if statement has moved from runtime (non-tree-shakable) to compile time (tree-shakable) position.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment