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

Commit a1e5cd5

Browse files
petebacondarwinvojtajina
authored andcommitted
feat($compile): allow disabling scope info
The compiler adds scope information (`ng-scope` CSS class and `$scope` data property) to elements when the are bound to the scope. This is mostly to aid debugging tools such as Batarang. In production this should be unnecesary and adds a performance penalty. In the bench/apps/largetable-bp this change caused an improvement of ~100ms (7%). This can be now disabled by calling `$compileProvider.debugInfoEnabled(false)` in a module `config` block: ``` someModule.config(['$compileProvider', function($compileProvider) { $compileProvider.debugInfoEnabled(false); }]); ``` In the bench/apps/largetable-bp benchmark this change, with debug info disabled, improved by ~120ms, that is ~10%. Measuring the "create" phase, 25 loops, mean time ~1200ms -> ~1080ms.
1 parent 3660fd0 commit a1e5cd5

File tree

2 files changed

+71
-33
lines changed

2 files changed

+71
-33
lines changed

src/ng/compile.js

+25-30
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,17 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
886886
}
887887
};
888888

889+
890+
function safeAddClass($element, className) {
891+
try {
892+
$element.addClass(className);
893+
} catch(e) {
894+
// ignore, since it means that we are trying to set class on
895+
// SVG element, where class name is read-only.
896+
}
897+
}
898+
899+
889900
var startSymbol = $interpolate.startSymbol(),
890901
endSymbol = $interpolate.endSymbol(),
891902
denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
@@ -896,11 +907,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
896907
NG_ATTR_BINDING = /^ngAttr[A-Z]/;
897908

898909
compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo(element, binding) {
899-
element
900-
.addClass('ng-binding')
901-
.data('$binding',
902-
(element.data('$binding') || []).concat(binding.expressions || [binding])
903-
);
910+
safeAddClass(element, 'ng-binding');
911+
element.data('$binding', (element.data('$binding') || []).concat(binding.expressions || [binding]));
912+
} : noop;
913+
914+
compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo(element, scope, isolated, noTemplate) {
915+
safeAddClass(jqLite(element), isolated ? 'ng-isolate-scope' : 'ng-scope');
916+
var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
917+
element.data ? element.data(dataName, scope) : jqLite.data(element, dataName, scope);
904918
} : noop;
905919

906920
return compile;
@@ -924,9 +938,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
924938
var compositeLinkFn =
925939
compileNodes($compileNodes, transcludeFn, $compileNodes,
926940
maxPriority, ignoreDirective, previousCompileContext);
927-
safeAddClass($compileNodes, 'ng-scope');
928-
var namespace = null;
941+
929942
return function publicLinkFn(scope, cloneConnectFn, transcludeControllers, parentBoundTranscludeFn, futureParentElement){
943+
var namespace = null;
930944
assertArg(scope, 'scope');
931945
if (!namespace) {
932946
namespace = detectNamespaceForChildElements(futureParentElement);
@@ -949,7 +963,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
949963
}
950964
}
951965

952-
$linkNode.data('$scope', scope);
966+
compile.$$addScopeInfo($linkNode, scope);
953967

954968
if (cloneConnectFn) cloneConnectFn($linkNode, scope);
955969
if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
@@ -967,15 +981,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
967981
}
968982
}
969983

970-
function safeAddClass($element, className) {
971-
try {
972-
$element.addClass(className);
973-
} catch(e) {
974-
// ignore, since it means that we are trying to set class on
975-
// SVG element, where class name is read-only.
976-
}
977-
}
978-
979984
/**
980985
* Compile function matches each node in nodeList against the directives. Once all directives
981986
* for a particular node are collected their compile functions are executed. The compile
@@ -1008,10 +1013,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
10081013
null, [], [], previousCompileContext)
10091014
: null;
10101015

1011-
if (nodeLinkFn && nodeLinkFn.scope) {
1012-
safeAddClass(attrs.$$element, 'ng-scope');
1013-
}
1014-
10151016
childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
10161017
!(childNodes = nodeList[i].childNodes) ||
10171018
!childNodes.length)
@@ -1062,7 +1063,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
10621063
if (nodeLinkFn) {
10631064
if (nodeLinkFn.scope) {
10641065
childScope = scope.$new();
1065-
jqLite.data(node, '$scope', childScope);
1066+
compile.$$addScopeInfo(node, childScope);
10661067
} else {
10671068
childScope = scope;
10681069
}
@@ -1563,14 +1564,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
15631564

15641565
isolateScope = scope.$new(true);
15651566

1566-
if (templateDirective && (templateDirective === newIsolateScopeDirective ||
1567-
templateDirective === newIsolateScopeDirective.$$originalDirective)) {
1568-
$element.data('$isolateScope', isolateScope);
1569-
} else {
1570-
$element.data('$isolateScopeNoTemplate', isolateScope);
1571-
}
1572-
1573-
1567+
compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
1568+
templateDirective === newIsolateScopeDirective.$$originalDirective)));
15741569

15751570
safeAddClass($element, 'ng-isolate-scope');
15761571

test/ng/compileSpec.js

+46-3
Original file line numberDiff line numberDiff line change
@@ -4250,12 +4250,17 @@ describe('$compile', function() {
42504250

42514251

42524252

4253-
it('should not leak if two "element" transclusions are on the same element', function () {
4253+
it('should not leak if two "element" transclusions are on the same element (with debug info)', function () {
42544254
if (jQuery) {
42554255
// jQuery 2.x doesn't expose the cache storage.
42564256
return;
42574257
}
42584258

4259+
4260+
module(function($compileProvider) {
4261+
$compileProvider.debugInfoEnabled(true);
4262+
});
4263+
42594264
inject(function($compile, $rootScope) {
42604265
expect(jqLiteCacheSize()).toEqual(0);
42614266

@@ -4277,12 +4282,48 @@ describe('$compile', function() {
42774282
});
42784283

42794284

4280-
it('should not leak if two "element" transclusions are on the same element', function () {
4285+
it('should not leak if two "element" transclusions are on the same element (without debug info)', function () {
42814286
if (jQuery) {
42824287
// jQuery 2.x doesn't expose the cache storage.
42834288
return;
42844289
}
42854290

4291+
4292+
module(function($compileProvider) {
4293+
$compileProvider.debugInfoEnabled(false);
4294+
});
4295+
4296+
inject(function($compile, $rootScope) {
4297+
expect(jqLiteCacheSize()).toEqual(0);
4298+
4299+
element = $compile('<div><div ng-repeat="x in xs" ng-if="x==1">{{x}}</div></div>')($rootScope);
4300+
expect(jqLiteCacheSize()).toEqual(0);
4301+
4302+
$rootScope.$apply('xs = [0,1]');
4303+
expect(jqLiteCacheSize()).toEqual(0);
4304+
4305+
$rootScope.$apply('xs = [0]');
4306+
expect(jqLiteCacheSize()).toEqual(0);
4307+
4308+
$rootScope.$apply('xs = []');
4309+
expect(jqLiteCacheSize()).toEqual(0);
4310+
4311+
element.remove();
4312+
expect(jqLiteCacheSize()).toEqual(0);
4313+
});
4314+
});
4315+
4316+
4317+
it('should not leak if two "element" transclusions are on the same element (with debug info)', function () {
4318+
if (jQuery) {
4319+
// jQuery 2.x doesn't expose the cache storage.
4320+
return;
4321+
}
4322+
4323+
module(function($compileProvider) {
4324+
$compileProvider.debugInfoEnabled(true);
4325+
});
4326+
42864327
inject(function($compile, $rootScope) {
42874328
expect(jqLiteCacheSize()).toEqual(0);
42884329
element = $compile('<div><div ng-repeat="x in xs" ng-if="val">{{x}}</div></div>')($rootScope);
@@ -5220,7 +5261,9 @@ describe('$compile', function() {
52205261
}));
52215262
});
52225263
inject(function($compile) {
5223-
element = $compile('<div transclude><div child></div></div>')($rootScope);
5264+
// We need to wrap the transclude directive's element in a parent element so that the
5265+
// cloned element gets deallocated/cleaned up correctly
5266+
element = $compile('<div><div transclude><div child></div></div></div>')($rootScope);
52245267
expect(capturedTranscludeCtrl).toBeTruthy();
52255268
});
52265269
});

0 commit comments

Comments
 (0)