AngularJS : Charger dynamiquement des fichiers (lazy loading)

Voici une petite astuce pratique si vous devez coder une « grosse » application avec AngularJS, appelée souvent « SPA » pour Single Page Application.

En effet, votre fichier HTML principal doit contenir les appels de tous les fichiers utiles à toute l’application. Or, ça prend de la place, c’est vilain, et consomme beaucoup de ressources. Car tout charger alors qu’on ne va pas naviguer sur toutes les pages, c’est pas terrible. Voici un exemple simple de Blog, avec une page qui liste tous les articles et une qui affiche un article particulier :

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>AngularJS</title>

        <!-- AngularJS -->
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.min.js"></script>
        <script src="https://code.angularjs.org/1.2.19/angular-route.min.js"></script>

        <!-- App -->
        <script src="app/app.js"></script>

        <!-- Services -->
        <script src="app/article/article.service.js"></script>
        
        <!-- Directives -->
        <script src="common/directives/button.directive.js"></script>
        
        <!-- Controllers -->
        <script src="app/article/controllers/articles.controller.js"></script>
        <script src="app/article/controllers/article.controller.js"></script>

    </head>

    <body data-ng-app="Blog">

        <div data-ng-view></div>

    </body>
</html>

On aurait les routes suivantes :

var app = angular.module('Blog', ['ngRoute']);

app.config(['$routeProvider', function ($routeProvider) {
    $routeProvider
        .when('/', {
            templateUrl: 'app/article/views/articles.html',
            controller: 'ArticlesController'
        })
        .when('/article/:id', {
            templateUrl: 'app/article/views/article.html',
            controller: 'ArticleController'
        })
        .otherwise({
            redirectTo: '/'
        });
}]);

Ici c’est propre. Mais dans le cas où c’est une grosse application, avec beaucoup de routes et de contrôleurs, côté performance, ça va chuter assez rapidement.

Partant de ce constat, on peut se dire : « Et pourquoi ne pas charger les fichiers lorsqu’on en a besoin ? »

C’est là qu’on parle de lazy loading. En effet, lorsque vous arrivez sur une page, une vue, vous avez besoin à minima d’un contrôleur et éventuellement de services et directives.

Lazy loading

Dans notre HTML on va simplement supprimer les appels spécifiques et ajouter une librairie pour arriver à nos fins :

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>AngularJS</title>

        <!-- AngularJS -->
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.min.js"></script>
        <script src="https://code.angularjs.org/1.2.19/angular-route.min.js"></script>

        <!-- Lazy loading module -->
        <script src="assets/libs/ocLazyLoading.min.js"></script>

        <!-- App -->
        <script src="app/app.js"></script>

    </head>

    <body data-ng-app="Blog">

        <div data-ng-view></div>

    </body>
</html>

Côté routage, on ajoute un peu plus de code :

var app = angular.module('Blog', ['ngRoute', 'oc.lazyLoad']);

app.config(['$routeProvider', function ($routeProvider) {

    $routeProvider
        .when('/', {
            templateUrl: 'app/article/views/articles.html',
            controller: 'ArticlesController',
            resolve: {
                lazy: ['$ocLazyLoad', function($ocLazyLoad) {
                    return $ocLazyLoad.load({
                        name: 'Blog',
                        files: [
                            'app/article/controllers/articles.controller.js',
                            'app/article/article.service.js',
                            'common/directives/button.directive.js'
                        ]
                    });
                }]
            }
        })
        .when('/article/:id', {
            templateUrl: 'app/article/views/article.html',
            controller: 'ArticleController',
            resolve: {
                lazy: ['$ocLazyLoad', function($ocLazyLoad) {
                    return $ocLazyLoad.load({
                        name: 'Blog',
                        files: [
                            'app/article/controllers/article.controller.js',
                            'app/article/article.service.js'
                        ]
                    });
                }]
            }
        })
        .otherwise({
            redirectTo: '/'
        });
}]);

C’est assez simple, on lui indique le nom de l’app concernée (« Blog »), ainsi qu’un tableau de fichiers. Ils seront chargées de façon asynchrone.

ocLazyLoad est sur GitHub : https://github.com/ocombe/ocLazyLoad