This is my lightning talk about performance considerations for Angular apps displaying lots of data.
It starts with a simplified explanation of $digest
and then gives a few
examples of how to apply this knowledge to cases when you're repeating over
a bunch of elements.
Make sure you know what's slow. Lots of tools that can help.
I'm not going to talk about them.
What causes slowness?
- Causes reflows
- Lots of strategies DOM manipulation
Angular is pretty good at helping you with this. In some special cases you can write your own directive to manipulate the DOM and improve performance.
The number of "dynamic things" your user sees.
This is called the number of $watch
s.
What adds a $watch
?
Consider the following chunk of an app:
<div>
<p>{{thing}}</p>
<p>{{getThing()}}</p>
<div ng-repeat="item in items">{{item.name}}</div>
</div>
Here's a list of the things Angular $watch
s for updates:
// angular figures this out based on directives, which add `$watch`s
var thingsThatMightUpdate = [
$scope.thing,
$scope.getThing(), // invoke this each time
// from ngRepeat
$scope.items.length,
$scope.items[0], // obj refs
$scope.items[1],
$scope.items[2],
$scope.items[3],
// from {{item.name}} inside ngRepeat
$scope.items[0].name, // values
$scope.items[1].name,
$scope.items[2].name,
$scope.items[3].name,
];
This is a simplification (cuz lightning talk). The documentation on scopes have the full story.
// map thingName to value
var oldValues = {
'$scope.thing': 'hello',
'$scope.items.length': 4,
/* ... */
};
function digest () {
var thingsThatChanged;
do {
thingsThatChanged = [];
thingsThatMightUpdate.forEach(function (thing) {
if (oldValue[thingName] !== $scope[thingName]) {
thingsThatChanged.push(thingName);
oldValue[thingName] = $scope[thingName];
}
});
// trigger `$watch`s, updating the DOM, etc
} while(thingsThatChanged.length > 0);
}
Note that we have to eval $scope.getThing()
on each digest.
If getThing()
is slow, then all your digests are slow.
If you have a bunch of things in the array, the digest will be slow.
tl;dr – watch fewer things, keep $watch
s fast.
$$$$$$$$$$$$$$$$$$$$$
Rather than use filters/functions in your bindings, transform the data before binding to it.
Controller:
angular.module('mySlowApp', []).controller('MyController', [
'$scope', '$http', function ($scope, $http) {
// this has like a million elts
$http.get('all-the-things.json').success(function (data) {
$scope.data = data;
});
$scope.transform = function (item) {
// do something expensive
return item;
};
}]);
HTML:
<div ng-repeat="item in data">{{transform(item.name)}}</div>
Controller:
angular.module('myFastApp', []).controller('MyController', [
'$scope', '$http', function ($scope, $http) {
// this has like a million elts
$http.get('all-the-things.json').success(function (data) {
$scope.data = data.map(transform);
});
function transform (item) {
// do something expensive
return item;
};
}]);
HTML:
<div ng-repeat="item in data">{{item.name}}</div>
Here are a few strategies.
ngRepeat over a subset of the things.
Controller:
angular.module('myApp', []).controller('MyController', [
'$scope', function ($scope) {
// this has like a million elts
$scope.bigArray = [ /* ... */ ];
$scope.page = 0;
$scope.$watch('page', function (index) {
$scope.littleArray = $scope.bigArray.slice(index, index + 5);
});
}]);
Template:
<div>
<div ng-repeat="item in littleArray">{{item.name}}</div>
<button ng-click="prev -= 1">prev</button>
<button ng-click="page += 1">next</button>
</div>
You can use a similar strategy but with infinite/virtual scrolling.
Coming to 1.3 Soon
3rd Party Modules
Opt out of data binding; use event listeners.
In your controller:
angular.module('myApp', []).controller('MyController', [
'$scope', '$interval', '$http', function ($scope, $interval, $http) {
// refresh every 10 seconds
$interval(function () {
// this has like a million elts
$http.get('all-the-things.json').success(function (data) {
$scope.$broadcast('dataUpdated', data);
});
}, 10000);
}]);
In your directive:
{
link: function (scope, elt) {
scope.$on('dataUpdated', function (ev, data) {
// do DOM manipulation
});
}
}
Angular makes the 95% case fast, but gives you the tools to take control of your own performance destiny when you want to.
MIT