Empêcher la fuite mémoire avec AngularJs et son $compile


Cette semaine, nous avons été confrontés à un problème particulier avec AngularJs.

Le navigateur affichait +500Mo de charge mémoire sur notre solution AngularJs, ce qui est bien trop avec un seul onglet.

En réalité, en observant les statistiques du nombre de watchers présents (avec ng-stat ou AngularJs Batarang) dans les différentes pages, nous avons découvert une fuite mémoire.

Chaque fois que nous cliquions sur un produit, 52 watchers s’ajoutaient aux déjà présents. Même comportement si nous revenions sur un produit sélectionné auparavant.

En réalité, la sélection d’un produit provoque un appel au serveur afin de récupérer le template destiné à être intégré dans un contrôle comprenant code html et directives éléments ou attributs AngularJs. Ce code sera donc interprété par le compile d’AngularJs.

Voici le code à ne pas faire !

this.displayP = function (data) {

            var parentElement = angular.element(‘#current_product’);

            var element = angular.element(data);

            $compile(element)($scope);

            if (parentElement.has(« li »).length) {

                // Remove the last product added to load the new One

                parentElement.empty();

            }

            parentElement.append(element);

            element.find(« .text-box »).trigger(« change »);

Ce qu’il faut corriger :
Détruire notre objet et watchers associés.
– Créer un nouveau scope depuis le courant afin de lui faire porter le produit (code interprété par le compile)
Ce qu’il fallait faire :
this.displayP = function (data) {
            var parentElement = angular.element(‘#current_product’);
            if (parentElement.has(« li »).length) {
                // Remove last product added to load the new one
                if (productInstance.childPScope != null)
                    productInstance.childPScope.$destroy();
                parentElement.empty();
            }
            var element = angular.element(data);
            var compiledDirectives = $compile(element);
            productInstance.childPScope = $scope.$new(false);
            compiledDirectives(productInstance.childPScope);
            parentElement.append(element);
            element.find(« .text-box »).trigger(« change »);

Finalement, l’instance détruite permet de supprimer les watchers inutiles et irrecupérables.

D’autres articles sur les performances dans AngularJs arriveront par la suite.

Nicolas DENIS

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s