AngularJS (version 1.x) applications, especially large ones, can benefit significantly from various optimization techniques. Here are 50 advanced tricks with detailed code examples and relevant links to improve performance:
1. Minimize Watchers
Details: AngularJS’s digest cycle checks every watcher in your application on every potential change. Reducing the number of watchers is paramount for performance.
// Bad: Watching a complex object
$scope.$watch('complexObject', function(newValue, oldValue) {
// ... heavy computation ...
}, true); // deep watch
// Good: Watching specific properties
$scope.$watch('complexObject.name', function(newValue, oldValue) {
// ... lighter computation based on name change ...
});
$scope.$watch('complexObject.age', function(newValue, oldValue) {
// ... lighter computation based on age change ...
});
2. Use One-Time Binding (::)
Details: For data that doesn’t need to be updated after initial rendering, use one-time binding to remove the watcher after the first digest cycle.
<div>{{ ::staticData }}</div>
3. Optimize Filters
Details: Filters can be expensive if not implemented efficiently. Avoid complex logic within filters and consider memoization for pure filters.
// Bad: Inefficient filter
.filter('expensiveFilter', function() {
return function(input) {
let result = [];
for (let i = 0; i < 1000; i++) {
// ... complex operation ...
}
// ... more complex operations ...
return result;
};
});
// Good: Simpler filter logic
.filter('simpleFilter', function() {
return function(input) {
return input ? input.toUpperCase() : '';
};
});
4. Limit $watchCollection
Details: `$watchCollection` performs a shallow comparison of the items in a collection. For large collections, this can be costly. If you only care about the reference changing, use `$watch` directly.
// Bad: Watching the collection content
$scope.$watchCollection('largeArray', function(newCollection, oldCollection) {
// ... logic on collection changes ...
});
// Good: Watching the collection reference
$scope.$watch('largeArray', function(newCollection, oldCollection) {
// ... logic only when the array reference changes ...
});
5. Debounce and Throttle Functions
Details: For events that fire frequently (e.g., `keyup`, `scroll`, `resize`), use debounce or throttle to limit the number of times your handler function is executed.
// Using $timeout for debounce
$scope.search = function(query) {
if ($scope.searchTimeout) {
$timeout.cancel($scope.searchTimeout);
}
$scope.searchTimeout = $timeout(function() {
// ... perform search ...
}, 300); // 300ms debounce
};
6. Optimize ng-repeat
Details: `ng-repeat` can be a major performance bottleneck for large lists. Use `track by $index` for simple arrays and `track by item.id` for objects with unique identifiers to help AngularJS efficiently track changes.
<li ng-repeat="item in largeArray track by $index">{{ item }}</li>
<li ng-repeat="item in largeObjectArray track by item.id">{{ item.name }}</li>
7. Virtual Scrolling for Large Lists
Details: For very long lists, only render the items that are currently visible in the viewport. Libraries like `angular-vs-repeat` can help implement this.
8. Avoid Deep Watching
Details: The third argument to `$watch` (the equality flag) defaults to `false` (reference equality). Setting it to `true` performs a deep comparison, which can be very expensive for complex objects.
// Avoid this if possible
$scope.$watch('complexObject', function(newValue, oldValue) {
// ...
}, true);
9. Detach Watchers When Not Needed
Details: If a part of your UI becomes inactive or a scope is destroyed, manually detach any watchers that are no longer necessary using the deregistration function returned by `$watch`.
var unwatch = $scope.$watch('someValue', function(newValue, oldValue) {
// ...
});
// Later, when the watcher is no longer needed:
unwatch();
10. Optimize Event Handlers
Details: Avoid complex logic directly within `ng-click` or other `ng-*` event handlers. Delegate the logic to controller methods.
<button ng-click="vm.doSomethingComplex()">Click Me</button>
// In the controller:
vm.doSomethingComplex = function() {
// ... complex logic ...
};
11. Use Controller As Syntax
Details: Using `controller as vm` promotes better organization and makes it clearer which scope properties belong to which controller, potentially improving performance and maintainability.
<div ng-controller="MyController as vm">
{{ vm.data }}
</div>
// In the controller:
angular.module('myApp').controller('MyController', function() {
var vm = this;
vm.data = 'Hello';
});
12. Avoid Anonymous Functions in Templates
Details: Calling anonymous functions directly in templates can lead to unnecessary function re-creation on each digest cycle. Call controller methods instead.
// Bad
<button ng-click="(data) => data.value + 1">Increment</button>
// Good
<button ng-click="vm.incrementValue(data)">Increment</button>
// In the controller:
vm.incrementValue = function(item) {
return item.value + 1;
};
13. Use `$compileProvider.debugInfoEnabled(false)` in Production
Details: Disabling debug info removes the data attributes that AngularJS adds to DOM elements for debugging purposes, reducing the size of the DOM and potentially improving rendering performance.
angular.module('myApp', [])
.config(function($compileProvider) {
$compileProvider.debugInfoEnabled(false);
});
14. Optimize DOM Manipulation
Details: Minimize direct DOM manipulation. Let AngularJS handle DOM updates through data binding. If you must manipulate the DOM, do it within directives and use techniques like document fragments for efficiency.
// In a directive's link function:
link: function(scope, element, attrs) {
var fragment = document.createDocumentFragment();
for (var i = 0; i < 100; i++) {
var li = document.createElement('li');
li.textContent = 'Item ' + i;
fragment.appendChild(li);
}
element.append(fragment); // Append the entire fragment at once
}
15. Use `ng-if` Instead of `ng-show`/`ng-hide` for Infrequent Elements
Details: `ng-show` and `ng-hide` only toggle the `display` CSS property, keeping the element and its watchers in the DOM. `ng-if` removes the element from the DOM entirely when the condition is false, reducing the number of watchers.
<div ng-if="isVisible">This element is rarely shown.</div>
<div ng-show="isVisible">This element is toggled frequently.</div>
16. Optimize Image Loading (Lazy Loading)
Details: Only load images when they are about to become visible in the viewport. Libraries or custom directives can implement lazy loading.
17. Use `$templateCache` for Static Templates
Details: Pre-load static templates into the `$templateCache` to avoid extra HTTP requests when they are needed.
angular.module('myApp').run(function($templateCache) {
$templateCache.put('myTemplate.html', '<div>Static Content</div>');
});
// In your directive or component:
templateUrl: 'myTemplate.html'
18. Optimize Third-Party Libraries
Details: Only include the parts of third-party libraries that you actually use. Some libraries offer modular builds or tree-shaking capabilities.
19. Profile Your Application
Details: Use browser developer tools (Performance tab) or AngularJS-specific profiling tools (e
20. Avoid Premature Optimization
Details: Don’t optimize code without identifying actual performance bottlenecks. Focus on the areas that are demonstrably slow based on profiling.
21. Use `$applyAsync` for Batching DOM Updates
Details: When triggering digest cycles outside of AngularJS’s knowledge (e.g., native event listeners), use `$applyAsync` to schedule the `$apply` call, allowing for batching of multiple updates into a single digest cycle.
element.addEventListener('click', function() {
scope.$applyAsync(function() {
scope.someValue = 'Updated';
});
});
22. Optimize String Interpolation
Details: Complex expressions within `{{ }}` can impact performance. Simplify expressions or move complex logic to controller methods.
// Bad
<div>{{ item.name.toUpperCase() + ' (' + (item.age > 18 ? 'Adult' : 'Minor') + ')' }}</div>
// Good
<div>{{ vm.formatItem(item) }}</div>
// In the controller:
vm.formatItem = function(item) {
return item.name.toUpperCase() + ' (' + (item.age > 18 ? 'Adult' : 'Minor') + ')';
};
23. Use `bindToController` in Directives
Details: Using `bindToController` in directives creates isolated scope properties that are directly bound to the controller, potentially simplifying scope management and improving performance in some scenarios.
.directive('myComponent', {
restrict: 'E',
scope: {
data: '='
},
controller: 'MyComponentController as vm',
bindToController: true,
template: '<div>{{ vm.data.name }}</div>'
});
angular.module('myApp').controller('MyComponentController', function() {
// this.data is directly bound to the 'data' attribute
});
24. Avoid Excessive DOM Elements
Details: A large DOM tree can slow down rendering and the digest cycle. Simplify your HTML structure and avoid unnecessary nested elements.
25. Optimize Routing
Details: Resolve only the data that is strictly necessary for a particular route before the view is rendered. Use `$routeProvider` or `$stateProvider` (from UI-Router) `resolve` properties.
$routeProvider.when('/users/:id', {
templateUrl: 'user-details.html',
controller: 'UserDetailsController',
resolve: {
user: function(UserService, $route) {
return UserService.getUser($route.current.params.id);
}
}
});
26. Use `$destroy` Event
Details: Listen for the `$destroy` event on scopes to perform cleanup tasks like unregistering event listeners or canceling timeouts, preventing memory leaks.
$scope.$on('$destroy', function() {
element.off('click', handleClick);
if (timeoutId) {
$timeout.cancel(timeoutId);
}
});
27. Consider Component-Based Architecture (Angular 1.5+)
Details: While still AngularJS 1.x, adopting a component-based architecture (introduced in 1.5) can lead to better organization and potentially improved performance through more isolated scopes.
angular.module('myApp').component('myGreeting', {
template: '<div>Hello, {{$ctrl.name}}!</div>',
bindings: {
name: '<'
},
controller: function() {
// ...
},
controllerAs: '$ctrl'
});
28. Optimize Data Binding with Immutable Data Structures
Details: When using immutable data structures, AngularJS can detect changes more efficiently by simply checking the object reference, potentially reducing the need for deep watches.
29. Batch API Calls
Details: Instead of making multiple small API requests, try to batch them into fewer, larger requests to reduce network overhead.
30. Use `$cacheFactory` for Caching Data
Details: Cache frequently accessed data using AngularJS’s built-in `$cacheFactory` service to avoid redundant API calls or computations.
angular.module('myApp').service('DataService', function($http, $cacheFactory) {
var cache = $cacheFactory('dataCache');
this.getData = function(id) {
var cachedData = cache.get(id);
if (cachedData) {
return $q.resolve(cachedData);
}
return $http.get('/api/data/' + id).then(function(response) {
cache.put(id, response.data);
return response.data;
});
};
});
31. Consider Server-Side Rendering (SSR)
Details: While more complex to implement with AngularJS 1.x, SSR can improve initial load times and SEO by rendering the initial HTML on the server.
32. Optimize Animations
Details: Use CSS transitions and animations instead of JavaScript-based animations whenever possible, as they are generally more performant.
33. Use Web Workers for Heavy Computations
Details: Offload computationally intensive tasks to Web Workers to prevent blocking the main thread and keep the UI responsive.
34. Minify and Gzip Your Assets
Details: Reduce the size of your HTML, CSS, JavaScript, and other assets by minifying them (removing unnecessary characters) and compressing them with Gzip for faster delivery over the network.
35. Use a Content Delivery Network (CDN)
Details: Serve static assets (images, scripts, styles) from a CDN to improve loading times by leveraging geographically distributed servers.
36. Optimize Fonts
Details: Use web-safe fonts or optimize custom font loading to prevent layout shifts and improve perceived performance.
37. Avoid Blocking JavaScript
Details: Structure your JavaScript to avoid blocking the rendering of the page. Use techniques like the `async` and `defer` attributes for script tags.
38. Optimize CSS Selectors
Details: Write efficient CSS selectors. Avoid overly specific or complex selectors that can slow down browser rendering.
39. Use `$compile` Sparingly
Details: `$compile` is powerful but can be expensive. Avoid dynamic compilation within loops or frequently executed code.
40. Consider Server-Side Pagination for Large Datasets
Details: For very large lists, implement pagination on the server and only fetch the data needed for the current page.
41. Use IndexedDB or Local Storage for Client-Side Caching
Details: For more persistent client-side caching than `$cacheFactory` provides, consider using IndexedDB or Local Storage.
42. Optimize Regular Expressions
Details: If you use regular expressions extensively, ensure they are efficient and avoid backtracking issues.
43. Debounce Input Fields with ng-model-options
Details: Use `ng-model-options=”{ debounce: 300 }”` to automatically debounce input field updates, reducing the frequency of `$watch` triggers.
<input type="text" ng-model="searchQuery" ng-model-options="{ debounce: 300 }">
44. Avoid Using `$rootScope` for Global State
Details: Excessive use of `$rootScope` can make your application harder to manage and test. Consider using services for sharing state across components.
45. Use `$document` and `$window` Sparingly
Details: Inject `$document` and `$window` only when necessary and avoid direct manipulation within controllers. Encapsulate DOM interactions within directives.
46. Consider WebSockets for Real-Time Data
Details: For applications with real-time data requirements, consider using WebSockets for more efficient bi-directional communication compared to frequent polling.
47. Optimize for Mobile Devices
Details: Consider mobile performance by optimizing images, minimizing network requests, and using touch-friendly UI elements.
48. Test Performance Regularly
Details: Include performance testing as part of your regular testing process to identify and address performance regressions early.
49. Stay Updated with AngularJS Best Practices (Even if Legacy)
Details: While AngularJS 1.x is no longer actively developed, understanding its best practices and common pitfalls remains crucial for maintaining existing applications.
50. Consider Migrating to a Newer Framework (Angular, React, Vue.js)
Details: For large, complex applications facing significant performance challenges, migrating to a more modern framework with built-in performance optimizations and a more efficient change detection mechanism might be the most effective long-term solution.
Applying these advanced optimization tricks can help you build and maintain performant AngularJS 1.x applications, even at scale. Remember to always profile your application to identify the most significant bottlenecks and focus your optimization efforts accordingly.
Leave a Reply