Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Commit

Permalink
perf($compile): Lazily compile the transclude function
Browse files Browse the repository at this point in the history
For transcluded directives, the transclude function can be lazily compiled
most of the time since the contents will not be needed until the
`transclude` function was actually invoked.  For example, the `transclude`
function that is passed to `ng-if` or `ng-switch-when` does not need to be
invoked until the condition that it's bound to has been matched.  For
complex trees or switch statements, this can represent significant
performance gains since compilation of branches is deferred, and that
compilation may never actually happen if it isn't needed.

There are two instances where compilation will not be lazy; when we scan
ahead in the array of directives to be processed and find at least two of
the following:

* A directive that is transcluded and does not allow multiple transclusion
* A directive that has templateUrl and replace: true

In both of those cases, we will need to continue eager compilation in
order to generate the multiple transclusion exception at the correct time.
  • Loading branch information
dcherman committed Jun 11, 2015
1 parent ffac747 commit 0f565e0
Showing 1 changed file with 56 additions and 2 deletions.
58 changes: 56 additions & 2 deletions src/ng/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -1625,6 +1625,37 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
};
}

/**
* A function generator that is used to support both eager and lazy compilation
* linking function.
* @param eager
* @param $compileNodes
* @param transcludeFn
* @param maxPriority
* @param ignoreDirective
* @param previousCompileContext
* @returns {Function}
*/
function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) {
if (eager) {
return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
}

var compiled;

return function() {
if (!compiled) {
compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);

// Null out all of these references in order to make them eligible for garbage collection
// since this is a potentially long lived closure
$compileNodes = transcludeFn = previousCompileContext = null;
}

return compiled.apply(this, arguments);
};
}

/**
* Once the directives have been collected, their compile functions are executed. This method
* is responsible for inlining directive templates as well as terminating the application
Expand Down Expand Up @@ -1669,6 +1700,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
replaceDirective = originalReplaceDirective,
childTranscludeFn = transcludeFn,
linkFn,
didScanForMultipleTransclusion = false,
mightHaveMultipleTransclusionError = false,
directiveValue;

// executes all directives on the current element
Expand Down Expand Up @@ -1711,6 +1744,27 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {

directiveName = directive.name;

// If we encounter a condition that can result in transclusion on the directive,
// then scan ahead in the remaining directives for others that may cause a multiple
// transclusion error to be thrown during the compilation process. If a matching directive
// is found, then we know that when we encounter a transcluded directive, we need to eagerly
// compile the `transclude` function rather than doing it lazily in order to throw
// exceptions at the correct time
if (!didScanForMultipleTransclusion && ((directive.templateUrl && directive.replace)
|| (directive.transclude && !directive.$$tlb))) {
var candidateDirective;

for (var scanningIndex = i + 1; candidateDirective = directives[scanningIndex++];) {
if ((candidateDirective.transclude && !candidateDirective.$$tlb)
|| (candidateDirective.templateUrl && candidateDirective.replace)) {
mightHaveMultipleTransclusionError = true;
break;
}
}

didScanForMultipleTransclusion = true;
}

if (!directive.templateUrl && directive.controller) {
directiveValue = directive.controller;
controllerDirectives = controllerDirectives || createMap();
Expand Down Expand Up @@ -1740,7 +1794,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
compileNode = $compileNode[0];
replaceWith(jqCollection, sliceArgs($template), compileNode);

childTranscludeFn = compile($template, transcludeFn, terminalPriority,
childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
replaceDirective && replaceDirective.name, {
// Don't pass in:
// - controllerDirectives - otherwise we'll create duplicates controllers
Expand All @@ -1754,7 +1808,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
} else {
$template = jqLite(jqLiteClone(compileNode)).contents();
$compileNode.empty(); // clear contents
childTranscludeFn = compile($template, transcludeFn);
childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn);
}
}

Expand Down

0 comments on commit 0f565e0

Please sign in to comment.