Note du traducteur: je suis la première fois que je traduit une langue étrangère, et mes mots sont inévitablement un peu obscurs, mais j'ai fait de mon mieux pour exprimer l'intention initiale de l'auteur et je n'avais pas beaucoup de polissage. La critique et la correction sont les bienvenues. De plus, cet article est long et dispose d'une grande quantité d'informations, qui peuvent être difficiles à digérer. Veuillez laisser un message pour discuter des détails. Cet article se concentre principalement sur l'optimisation des performances de V8, et une partie du contenu n'est pas applicable à tous les moteurs JS. Enfin, veuillez indiquer la source lors de la réimpression :)
===============================================================.
De nombreux moteurs JavaScript, tels que le moteur V8 de Google (utilisé par Chrome et Node), sont conçus spécifiquement pour les grandes applications JavaScript qui nécessitent une exécution rapide. Si vous êtes un développeur et que vous êtes préoccupé par l'utilisation de la mémoire et les performances de la page, vous devez comprendre comment fonctionne le moteur JavaScript de votre navigateur. Qu'il s'agisse de V8, Spidermonkey (Firefox) Carakan (Opera), Chakra (IE) ou d'autres moteurs, cela peut vous aider à mieux optimiser votre application . Cela ne signifie pas que vous devez optimiser spécifiquement pour un certain navigateur ou moteur, et ne jamais le faire.
Cependant, vous devriez vous poser quelques questions:
Un site Web de chargement rapide est comme une voiture de sport rapide qui nécessite des pièces spécialement personnalisées. Source de l'image: dhybridcars.
Il y a des pièges communs lors de l'écriture de code haute performance, et dans cet article, nous montrerons des moyens éprouvés et meilleurs d'écrire du code.
Si vous n'avez pas une compréhension approfondie des moteurs JS, il n'y a aucun problème à développer une grande application Web, tout comme une personne qui peut conduire n'a vu que le capot mais pas le moteur à l'intérieur du capot. Étant donné que Chrome est le premier choix de mon navigateur, parlons de son moteur JavaScript. Le V8 est composé des parties centrales suivantes:
La collecte des ordures est une forme de gestion de la mémoire , qui est en fait un concept de collecteur, essayant de recycler la mémoire occupée par des objets qui ne sont plus utilisés. Dans une langue de collecte des ordures comme JavaScript, les objets qui sont toujours référencés dans l'application ne seront pas effacés.
L'élimination manuelle des références d'objets n'est pas nécessaire dans la plupart des cas. Tout fonctionnera très bien en mettant simplement les variables où ils sont nécessaires (idéalement, aussi étendus localement que possible, c'est-à-dire la fonction qu'ils sont utilisés au lieu de la couche externe de la fonction).
Le collecteur de déchets tente de recycler la mémoire. Source de l'image: Valtteri Mäki.
En JavaScript, il est impossible de forcer la collecte des ordures. Vous ne devriez pas le faire car le processus de collecte des ordures est contrôlé par l'exécution et il sait quel est le meilleur moment pour nettoyer.
Il existe de nombreuses discussions sur le recyclage de la mémoire JavaScript sur Internet sur la suppression du mot-clé. Bien qu'il puisse être utilisé pour supprimer les attributs (touches) dans les objets (MAP), certains développeurs pensent qu'il peut être utilisé pour forcer les "déréférences". Il est recommandé d'éviter d'utiliser la suppression chaque fois que possible. Dans l'exemple ci-dessous delete ox 的弊大于利,因为它改变了o的隐藏类,并使它成为一个"慢对象"。
var o = {x: 1}; supprimer le bœuf; // vrai bœuf; // indéfiniVous trouverez facilement la suppression des références dans les bibliothèques JS populaires - c'est linguistiquement utile. Il convient de noter ici qui évite de modifier la structure de l'objet "chaud" lors de l'exécution. Le moteur JavaScript peut détecter de tels objets "chauds" et essayer de les optimiser. Si la structure de l'objet ne change pas de manière significative pendant le cycle de vie, le moteur sera plus facile à optimiser l'objet, et l'opération de suppression déclenchera en fait ce changement structurel plus important, ce qui n'est pas propice à l'optimisation du moteur.
Il y a aussi des malentendus sur le fonctionnement nul. Définir une référence d'objet à NULL ne rend pas l'objet "vide", il définit simplement sa référence à vide. L'utilisation d'OX = null est meilleure que d'utiliser la suppression, mais ce n'est peut-être pas nécessaire.
var o = {x: 1}; o = null; o; // nullo.x // TypeErrorSi cette référence est la dernière référence à l'objet actuel, l'objet sera collecté aux ordures. Si cette référence n'est pas la dernière référence à l'objet actuel, l'objet est accessible et ne sera pas collecté aux ordures.
Il convient également de noter que les variables globales ne sont pas nettoyées par le collecteur des ordures pendant le cycle de vie de la page. Peu importe combien de temps la page est ouverte, les variables dans la portée de l'objet global existeront toujours lorsque le JavaScript s'exécutera.
var myGlobalNamespace = {};Les objets globaux ne seront nettoyés que lors de la rafraîchissement de la page, de la navigation vers une autre page, de la fermeture de l'onglet ou de la sortie du navigateur. Les variables de la portée de la fonction seront nettoyées lorsqu'elles seront hors de portée, c'est-à-dire que lorsque la fonction est sortie, il n'y a pas de références et de telles variables seront nettoyées.
Pour que le collecteur des ordures puisse collecter autant d'objets que possible, ne contiennent pas d'objets qui ne sont plus utilisés . Voici quelques éléments à retenir:
Ensuite, parlons des fonctions. Comme nous l'avons déjà dit, la collecte des ordures fonctionne en recyclant des blocs de mémoire (objets) qui ne sont plus accessibles. Pour mieux illustrer cela, voici quelques exemples.
fonction foo () {var bar = new LargeObject (); bar.someCall ();}Lorsque FOO revient, l'objet pointé par Bar sera automatiquement recyclé par le collecteur des ordures car il n'a pas de références existantes.
Comparer:
fonction foo () {var bar = new LargeObject (); bar.somecall (); barre de retour;} // ailleurs var b = foo ();Maintenant, nous avons une référence pointant vers l'objet de barre, de sorte que le cycle de vie de l'objet Bar continue de l'appel à Foo jusqu'à ce que l'appelant spécifie une autre variable B (ou B est hors de portée).
Lorsque vous voyez une fonction, renvoyez une fonction interne, qui sortira de l'accès à la portée, même après l'exécution de la fonction externe. Il s'agit d'une fermeture de base - une expression de variables qui peuvent être définies dans un contexte spécifique. Par exemple:
fonction sum (x) {fonction sumit (y) {return x + y; }; return sumit;} // usagevar suma = sum (4); var sumb = suma (3); console.log (sumb); // retourne 7L'objet de fonction (Sumit) généré dans le contexte de l'appel de somme ne peut pas être recyclé. Il est référencé par la variable globale (SUMA) et peut être appelé via SUMA (n).
Jetons un coup d'œil à un autre exemple, où pouvons-nous accéder au plus grand plus grand?
var a = function () {vargestr = nouveau tableau (1000000) .join ('x'); return function () {returngestr; };} ();Oui, nous pouvons accéder à Gredestr via A (), donc il n'est pas recyclé. Et ce qui suit?
var a = function () {var smallstr = 'x'; vargestr = nouveau tableau (1000000) .join ('x'); return function (n) {return smallstr; };} ();Nous ne pouvons plus accéder à Gregestr, c'est déjà un candidat à la collection d'ordures. [Note du traducteur: parce que le plus grand n'a plus de références externes]
L'un des pires endroits pour fuir la mémoire est dans la boucle, ou dans setTimeout () / setInterval (), mais cela est assez courant. Pensez aux exemples suivants:
var myoBj = {callMemaybe: function () {var myref = this; var val = setTimeout (function () {console.log ('le temps est épuisé!'); myref.callMemaybe ();}, 1000); }}; Si nous exécutons myObj.CallMemaybe (); Pour démarrer la minuterie, nous pouvons voir que la console imprime "le temps s'épuise!" chaque seconde. Si myObj = null,定时器依旧处于激活状态。为了能够持续执行,闭包将myObj传递给setTimeout,这样myObj是无法被回收的。相反,它引用到myObj的因为它捕获了myRef。这跟我们为了保持引用将闭包传给其他的函数是一样的。
Il convient également de rappeler que les références dans les appels SetTimeout / SetInterval (telles que les fonctions) devront être exécutées et terminées avant de pouvoir être collectées à la poubelle.
N'optimisez jamais le code jusqu'à ce que vous en ayez vraiment besoin. Maintenant, vous pouvez souvent voir des repères qui montrent que N est plus optimisé dans le V8 que M, mais si vous le testez dans le code ou l'application du module, vous constaterez que ces optimisations sont vraiment beaucoup plus petites que vous vous attendez.
Il vaut mieux ne rien faire que de le faire trop. Source de l'image: Tim Sheerman-Chase.
Par exemple, nous voulons créer un tel module:
Il existe plusieurs facteurs différents dans ce problème, bien qu'il soit également facile à résoudre. Comment stockons-nous les données, comment dessiner efficacement les tables et les ajouter au DOM et comment gérer mieux les événements de table?
L'approche initiale (naïve) pour faire face à ces problèmes consiste à utiliser des objets pour stocker les données et les mettre dans un tableau, utiliser jQuery pour traverser les données pour dessiner une table et l'ajouter au DOM, et enfin utiliser la liaison des événements au comportement de clic que nous attendons.
Remarque: ce n'est pas ce que vous devez faire
var modulea = function () {return {data: dataArrayObject, init: function () {this.addtable (); this.addevents (); }, addTable: function () {for (var i = 0; i <lignes; i ++) {$ tr = $ ('<tr> </tr>'); pour (var j = 0; j <this.data.length; j ++) {$ tr.append ('<td>' + this.data [j] ['id'] + '</td>'); } $ tr.appendto ($ tBody); }}, addvents: function () {$ ('table td'). sur ('click', function () {$ (this) .toggleclass ('active');}); }};} ();Ce code complète simplement et efficacement la tâche.
Mais dans ce cas, les données que nous traversons sont juste un ID de propriété numérique qui aurait dû être simplement stocké dans le tableau. Fait intéressant, il est préférable d'utiliser directement les méthodes DocumentFragment et Local DOM que de générer des tables en utilisant jQuery (de cette manière), et bien sûr, le proxy d'événements a des performances plus élevées que la liaison de chaque TD seul.
Notez que bien que JQuery utilise le document de document en interne, dans notre exemple, les appels de code ajoutent dans une boucle et ces appels impliquent d'autres connaissances, donc l'effet d'optimisation ici n'est pas très bon. J'espère que ce ne sera pas un point de douleur, mais assurez-vous de faire une référence pour vous assurer que votre code est correct.
Pour notre exemple, les pratiques ci-dessus apportent des améliorations de performances (souhaitées). Le proxyage des événements est une amélioration de la liaison simple, et le document de fragment facultatif aide également.
var moduled = function () {return {data: dataArray, init: function () {this.addtable (); this.addevents (); }, addtable: function () {var td, tr; var fragment = document.CreateDocumentFragment (); var fragment2 = document.CreateDocumentFragment (); pour (var i = 0; i <lignes; i ++) {tr = document.CreateElement ('tr'); pour (var j = 0; j <this.data.length; j ++) {td = document.createElement ('td'); td.appendChild (document.CreateTExtNode (this.data [j])); frag2.ApendChild (TD); } tr.appendChild (frag2); frag.ApendChild (tr); } tbody.appendChild (frag); }, addvents: function () {$ ('table'). on ('click', 'td', function () {$ (this) .toggleclass ('active');}); }};} ();Jetons un coup d'œil à d'autres moyens d'améliorer les performances. Vous avez peut-être lu que l'utilisation du mode prototype est meilleur que le mode module, ou que vous avez entendu dire que l'utilisation de cadres de modèle JS fonctionne mieux. Parfois, cela est vrai, mais ils sont utilisés pour rendre le code plus lisible. Soit dit en passant, il y a une précompilation! Voyons comment il fonctionne dans la pratique?
moduleg = function () {}; moduleg.prototype.data = dataArray; moduleg.prototype.init = function () {this.addtable (); this.addevents ();}; moduleg.prototype.addtable = function () {var template = _.Template ($ ('# template'). text ()); var html = template ({'data': this.data}); $ tbody.append (html);}; moduleg.prototype.addevents = function () {$ ('table'). on ('click', 'td', function () {$ (this) .toggleclass ('active');});}; var modg = new moduleg ();Il s'avère que les améliorations des performances apportées dans cette situation sont négligeables. Le choix des modèles et des prototypes n'offre rien de plus. C'est-à-dire que les performances ne sont pas la raison pour laquelle les développeurs les utilisent, et la lisibilité, le modèle d'héritage et la maintenabilité apportés au code sont les vraies raisons.
Des problèmes plus complexes incluent des images de la toile efficacement sur le canevas et la manipulation des données de pixels avec ou sans tableaux de types.
Avant d'utiliser certaines méthodes pour votre propre application, assurez-vous d'en savoir plus sur l'analyse comparative de ces solutions. Peut-être que quelqu'un se souvient encore du tir et des extensions ultérieures du modèle JS. Vous devez comprendre que l'analyse comparative n'existe pas dans les applications virtuelles que vous ne pouvez pas voir, mais devez tester les optimisations apportées par votre code réel.
Les points d'optimisation de chaque moteur V8 sont introduits en détail en dehors de la portée de cet article. Bien sûr, il y a de nombreux conseils à mentionner ici. N'oubliez pas ces conseils et vous pouvez réduire le code qui a de mauvaises performances.
fonction add (x, y) {return x + y;} add (1, 2); ajouter ('a', 'b'); Add (my_custom_object, undefined);Pour plus de contenu, veuillez consulter le partage de Daniel Clifford sur Google E / S. Briser la limite de vitesse JavaScript avec V8. Optimiser pour V8 - Une série vaut également la peine d'être lue.
Il n'y a qu'une seule différence principale entre les objets et les tableaux en JavaScript, c'est-à-dire la propriété de longueur magique des tableaux. Si vous maintenez cette propriété vous-même, les objets et les tableaux en V8 sont aussi rapides que ceux des tableaux.
Le clonage des objets est un problème courant pour les développeurs d'applications. Bien que divers repères puissent prouver que V8 gère bien ce problème, soyez prudent. Copier de grandes choses est généralement plus lent - ne faites pas cela. La boucle for..in en js est particulièrement mauvaise car elle a une spécification démoniaque et peut ne jamais être plus rapide que n'importe quel objet dans un moteur.
Lorsque vous êtes sûr de copier des objets sur le chemin de code de performance critique, utilisez un tableau ou une fonction "Copier le constructeur" personnalisé pour copier explicitement chaque propriété. C'est probablement le moyen le plus rapide:
Clone de fonction (original) {this.foo = original.foo; this.bar = original.bar;} var copy = new clone (original);Les fonctions de mise en cache lors de l'utilisation du mode module peuvent entraîner des améliorations des performances. Voir l'exemple ci-dessous, car il crée toujours une nouvelle copie de la fonction membre, les modifications que vous voyez peuvent être plus lentes.
Notez également que l'utilisation de cette méthode est évidemment meilleure, pas seulement en s'appuyant sur le mode prototype (confirmé par le test JSPERF).
Améliorations des performances lors de l'utilisation du mode module ou du mode prototype
Il s'agit d'un test de comparaison de performances du mode prototype et du mode module:
// Prototype Match klass1 = function () {} klass1.prototype.foo = function () {log ('foo'); } Klass1.prototype.bar = function () {log ('bar'); } // module du module klass2 = fonction () {var foo = function () {log ('foo'); }, bar = function () {log ('bar'); }; return {foo: foo, bar: bar}} // module du module avec fonctions caches var foofonction = function () {log ('foo'); }; var barfunction = function () {log ('bar'); }; Klass3 = function () {return {foo: foOfonction, bar: barfunction}} // itération tests // prototypal var i = 1000, objs = []; while (i--) {var o = new Klass1 () objs.push (new Klass1 ()); O.bar; O.foo; } // module module var i = 1000, objs = []; while (i--) {var o = new Klass1 () objs.push (new Klass1 ()); O.bar; O.foo; } // module module var i = 1000, objs = []; while (i--) {var o = klass2 () objs.push (klass2 ()); O.bar; O.foo; } // motif de module avec fonctions caches var i = 1000, objs = []; while (i--) {var o = klass3 () objs.push (klass3 ()); O.bar; O.foo; } // Voir le test pour plus de détailsEnsuite, parlons des techniques liées aux tableaux. En général, ne supprimez pas les éléments du tableau , ce qui fera la transition de la matrice vers une représentation interne plus lente. Lorsque l'indice devient clairsemé, V8 transforme l'élément en un motif de dictionnaire plus lent.
Les littéraux de tableau sont très utiles et il peut faire allusion à la taille et au type du réseau VM. Il est généralement utilisé dans les tableaux de petites tailles.
// Ici, V8 peut voir que vous voulez un tableau à 4 éléments contenant des nombres: var a = [1, 2, 3, 4]; // ne faites pas ceci: a = []; // Ici V8 ne sait rien de l'arrayfor (var i = 1; i <= 4; i ++) {a.push (i);}Il n'est en aucun cas une bonne idée de stocker des données de types mixtes (tels que les nombres, les chaînes, non définis, vrais / faux) dans un tableau. Par exemple var arr = [1, «1», non défini, vrai, «true»]
Test de performance de l'inférence de type
Comme nous l'avons vu, les tableaux d'entiers sont les plus rapides.
Lorsque vous utilisez des tableaux clairsemés, veillez à l'accès aux éléments sera beaucoup plus lent que les tableaux complets. Parce que V8 n'allouera pas un espace entier à un tableau qui n'utilise qu'une partie de l'espace. Au lieu de cela, il est géré dans un dictionnaire, économisant les deux espaces mais prenant le temps d'accès.
Test de tableaux clairsemés et de tableaux complets
Ne pré-allez pas de gros tableaux (comme des éléments supérieurs à 64k), leur taille maximale, mais doit être alloué dynamiquement. Avant de tester nos performances dans cet article, n'oubliez pas que cela ne s'applique qu'à certains moteurs JavaScript.
Les littéraux vides et les tableaux pré-alloués sont testés dans différents navigateurs
Nitro (Safari) est plus bénéfique pour les tableaux pré-allocés. Dans d'autres moteurs (V8, Spidermonkey), la pré-allocation n'est pas efficace.
Test de tableau pré-prolocuté
// vide Arrayvar arr = []; for (var i = 0; i <1000000; i ++) {arr [i] = i;} // arrayvar pré-allocalisé arr = nouveau tableau (1000000); pour (var i = 0; i <1000000; i ++) {arr [i] = i;}Dans le monde des applications Web, la vitesse est tout. Aucun utilisateur ne souhaite utiliser une application de table qui prend quelques secondes pour calculer le nombre total d'une colonne ou pour résumer les informations. C'est une raison importante pour laquelle vous voulez serrer chaque élément de performance dans votre code.
Source de l'image: par Olof Forsberg.
Comprendre et améliorer les performances de votre application est très utile, mais c'est également difficile. Nous recommandons les étapes suivantes pour résoudre les points de douleur de performance:
Certains des outils et techniques recommandés ci-dessous peuvent vous aider.
Il existe de nombreuses façons d'exécuter une référence pour les extraits de code JavaScript pour tester ses performances - l'hypothèse générale est que la référence compare simplement deux horodatages. Ce modèle est signalé par l'équipe JSPERF et est utilisé dans la suite de référence de Sunspider et Kraken:
var TotalTime, start = new Date, iterations = 1000; while (iterations--) {// Snippet de code va ici} // TotalTime → Le nombre de MIMIONIONDEDS TRAPIS // pour exécuter l'extrait de code 1000 1000 TimeStotaltime = new Date - Start;Ici, le code à tester est placé dans une boucle et exécute un nombre défini de fois (par exemple, 6 fois). Après cela, la date de début est soustraite de la date de fin, et le temps nécessaire pour effectuer l'opération dans la boucle est dérivé.
Cependant, cette analyse comparative fait des choses trop simples, surtout si vous voulez exécuter des repères sur plusieurs navigateurs et environnements. Le collecteur des ordures lui-même a un certain impact sur les résultats. Même si vous utilisez une solution comme Window.Performance, ces lacunes doivent être prises en compte.
Que vous exécutiez uniquement la partie de référence du code, rédigez une suite de tests ou que vous codez la bibliothèque de référence, les repères JavaScript sont en fait plus que vous ne le pensez. Pour des références de guide plus détaillées, je vous recommande fortement de lire les repères JavaScript fournis par Mathias Bynens et John-David Dalton.
Les outils de développeur Chrome ont une bonne prise en charge pour JavaScript Analytics. Vous pouvez utiliser cette fonctionnalité pour détecter les fonctions qui prennent la plupart du temps afin que vous puissiez les optimiser. Ceci est important, même de petits changements dans le code peuvent avoir un impact significatif sur les performances globales.
Panneau d'analyse des outils de développeur Chrome
Le processus d'analyse commence à obtenir la ligne de base des performances du code, puis le manifeste sous la forme d'une chronologie. Cela nous dira combien de temps le code prendra pour fonctionner. L'onglet Profils nous donne une meilleure perspective sur ce qui se passe dans l'application. Les fichiers d'analyse CPU JavaScript montrent la quantité de temps de CPU utilisé dans notre code, les fichiers d'analyse de sélecteur CSS montrent combien de temps est consacré au traitement des sélecteurs, et les instantanés de tas montrent la quantité de mémoire utilisée dans nos objets.
Avec ces outils, nous pouvons séparer, ajuster et réanalyser pour mesurer si nos optimisations de performances fonctionnelles ou opérationnelles sont réellement efficaces.
L'onglet de profil affiche les informations sur les performances du code.
Une bonne introduction à l'analyse, lisez le profil JavaScript de Zack Grossbart avec les outils de développeur Chrome.
CONSEIL: Idéalement, si vous souhaitez vous assurer que votre analyse n'est affectée par aucune application ou extension installée, vous pouvez utiliser l'indicateur --user-data-dir <empty_directory> pour démarrer Chrome. Dans la plupart des cas, ce test d'optimisation de la méthode devrait être suffisant, mais il vous faut également plus de temps. C'est ce que le logo V8 peut aider.
À l'intérieur de Google, les outils de développeur Chrome sont largement utilisés par des équipes telles que Gmail pour aider à détecter et à dépanner les fuites de mémoire.
Statistiques de la mémoire dans les outils de développeur chromé
La mémoire compte l'utilisation de la mémoire privée, la taille du tas JavaScript, le nombre de nœuds DOM, le nettoyage de stockage, les comptoirs d'écoute d'événements et les collectionneurs de déchets qui concernent notre équipe. La technologie «3 instantanés» de la lecture de Loreena Lee. Le point clé de cette technique consiste à enregistrer un comportement dans votre application, à forcer la collecte des ordures, à vérifier si le nombre de nœuds DOM a été restauré à la ligne de base attendue, puis à analyser les instantanés des trois tas pour déterminer s'il y a une fuite de mémoire.
La gestion de la mémoire des applications à page unique (comme AngularJS, Backbone, Ember) est très importante, elles ne rafraîchaient presque jamais la page. Cela signifie que les fuites de mémoire peuvent être assez évidentes. Les applications à une seule page sur les terminaux mobiles sont pleines de pièges car l'appareil a une mémoire limitée et exécute des applications telles que des clients de messagerie ou des réseaux sociaux pendant longtemps. Plus la capacité est grande, plus la responsabilité est lourde.
Il existe de nombreuses façons de résoudre ce problème. Dans la colonne vertébrale, assurez-vous d'utiliser Dispose () pour gérer les anciennes vues et références (actuellement disponibles dans Backbone (Edge). Cette fonction est récemment ajoutée, supprimant le gestionnaire ajouté à l'objet "Event" de la vue, et l'écouteur d'événements via le modèle ou la collection du troisième paramètre (Cappel Context) passé à la vue. L'auditeur pour éviter les fuites de mémoire lorsqu'ils détectent que les éléments sont supprimés.
Quelques conseils judicieux de Derick Bailey:
Au lieu de comprendre comment fonctionnent les événements et les références, suivez les règles standard pour gérer la mémoire en JavaScript. Si vous souhaitez charger des données dans une collection d'épine dorsale pleine d'objets utilisateur, vous souhaitez effacer la collection afin qu'elle ne prenne plus de mémoire, alors toutes les références à la collection et les références aux objets de la collection sont nécessaires. Une fois que la référence utilisée est claire, la ressource est recyclée. Il s'agit des règles standard de collecte des ordures JavaScript.
Dans l'article, Derick couvre de nombreux défauts de mémoire courants lors de l'utilisation de Backbone.js et comment résoudre ces problèmes.
Le tutoriel sur les fuites de mémoire de débogage dans le nœud par Felix Geisendörfer mérite également d'être lu, surtout lorsqu'il fait partie d'une pile de spa plus large.
Lorsque le navigateur redevient des éléments dans un document, ils doivent être recalculés et leurs positions et géométrie, que nous appelons reflux. Reflow bloque les opérations des utilisateurs dans le navigateur, il est donc très utile de comprendre que l'amélioration du temps de reflux est améliorée.
Charge temporaire de reflux
Vous devez déclencher une reflux ou redessiner les lots, mais utiliser ces méthodes avec modération. Il est également important d'essayer de ne pas traiter avec Dom. Vous pouvez utiliser DocumentFragment, un objet de document léger. Vous pouvez l'utiliser comme un moyen d'extraire une partie de l'arbre de document ou créer un nouveau document "fragment". Au lieu d'ajouter constamment des nœuds DOM, il est préférable d'effectuer des opérations d'insertion DOM une seule fois après avoir utilisé le fragment de document pour éviter une reflux excessive.
Par exemple, nous écrivons une fonction pour ajouter 20 divs à un élément. Si vous ajoutez simplement un div à l'élément à chaque fois, cela déclenchera 20 reflétés.
fonction adddivs (élément) {var div; pour (var i = 0; i <20; i ++) {div = document.createElement ('div'); div.innerhtml = 'Heya!'; element.ApendChild (div); }}Pour résoudre ce problème, nous pouvons utiliser DocumentFragment à la place, nous pouvons y ajouter un nouveau div à la fois. L'ajout de DocumentFragment au DOM après l'achèvement ne déclenchera une reflux qu'une seule fois.
fonction adddivs (élément) {var div; // crée un nouveau document de fragment vide. var fragment = document.CreateDocumentFragment (); pour (var i = 0; i <20; i ++) {div = document.createElement ('a'); div.innerhtml = 'Heya!'; fragment.ApendChild (div); } element.appendChild (fragment);}Voir Rendre le Web plus rapide, l'optimisation de la mémoire JavaScript et la recherche de fuites de mémoire.
Pour aider à découvrir des fuites de mémoire JavaScript, Google Developers (Marja Hölttä et Jochen Eisinger) a développé un outil qui fonctionne en conjonction avec des outils de développeur Chrome pour récupérer des instantanés du tas et détecter les objets qui provoquent la fuite de mémoire.
Un outil de détection de fuite de mémoire JavaScript
Il existe un article complet sur la façon d'utiliser cet outil. Il est recommandé d'aller à la page du projet de détecteur de fuite de mémoire pour vous-même.
Si vous voulez savoir pourquoi ces outils n'ont pas été intégrés dans nos outils de développement, il y a deux raisons. Il a été initialement conçu pour nous aider à capturer certains scénarios de mémoire spécifiques dans la bibliothèque de fermeture, ce qui convient plus en tant qu'outil externe.
Chrome prend en charge le passage de certains drapeaux directement à V8 pour des résultats de sortie d'optimisation du moteur plus détaillés. Par exemple, cela peut suivre l'optimisation de V8:
"/ Applications / google chrome / google chrome" --js-fllags = "- trace-opt --trace-deopt"
Les utilisateurs de Windows peuvent exécuter chrome.exe js-fllags = "trace-opt trace-deopt"
Lors du développement d'une application, le logo V8 ci-dessous peut être utilisé.
Le script de traitement de V8 utilise * (astérisque) pour identifier les fonctions optimisées et utilise ~ (wavy) pour représenter des fonctions non optimisées.
Si vous souhaitez en savoir plus sur le logo du V8 et le fonctionnement de l'intérieur du V8, il est fortement recommandé de lire l'excellent article de Vyacheslav Egorov sur les internes V8.
Le temps de haute précision (HRT) est une interface de temps de haute précision de niveau submilliseconde qui ne donne aucun impact sur le temps du système et les ajustements des utilisateurs. Il peut être considéré comme une méthode de mesure plus précise que la nouvelle date et la date de la date. Cela nous aide beaucoup à écrire des références.
Le temps de haute précision (HRT) fournit une précision actuelle de temps inférieur au milliseconde
Actuellement, HRT est utilisé dans Chrome (version stable) dans Window.Performance.WebkitNow (), mais le préfixe est jeté dans Chrome Canary, ce qui permet d'appeler via Window.performance.Now (). Paul Irish a publié plus sur HRT dans HTML5ROCKS.
Maintenant que nous connaissons l'heure précise actuelle, y a-t-il une API qui peut mesurer avec précision les performances de la page? Well, now there is a Navigation Timing API that provides an easy way to get accurate and detailed time measurement records when web pages are loaded and presented to users. You can use window.performance.timing in console to get time information:
显示在控制台中的时间信息
我们可以从上面的数据获取很多有用的信息,例如网络延时为responseEnd fetchStart,页面加载时间为loadEventEnd responseEnd,处理导航和页面加载的时间为loadEventEnd navigationStart。
正如你所看到的,perfomance.memory的属性也能显示JavaScript的内存数据使用情况,如总的堆大小。
更多Navigation Timing API的细节,阅读Sam Dutton的Measuring Page Load Speed With Navigation Timing。
Chrome中的about:tracing提供了浏览器的性能视图,记录了Chrome的所有线程、tab页和进程。
About:Tracing提供了浏览器的性能视图
这个工具的真正用处是允许你捕获Chrome的运行数据,这样你就可以适当地调整JavaScript执行,或优化资源加载。
Lilli Thompson有一篇写给游戏开发者的使用about:tracing分析WebGL游戏的文章,同时也适合JavaScript的开发者。
在Chrome的导航栏里可以输入about:memory,同样十分实用,可以获得每个tab页的内存使用情况,对定位内存泄漏很有帮助。
我们看到, JavaScript的世界中有很多隐藏的陷阱,且并没有提升性能的银弹。只有把一些优化方案综合使用到(现实世界)测试环境,才能获得最大的性能收益。即便如此,了解引擎是如何解释和优化代码,可以帮助你调整应用程序。
测量,理解,修复。不断重复这个过程。
图片来源: Sally Hunter
谨记关注优化,但为了便利可以舍弃一些很小的优化。例如,有些开发者选择.forEach和Object.keys代替for和for..in循环,尽管这会更慢但使用更方便。要保证清醒的头脑,知道什么优化是需要的,什么优化是不需要的。
同时注意,虽然JavaScript引擎越来越快,但下一个真正的瓶颈是DOM。回流和重绘的减少也是重要的,所以必要时再去动DOM。还有就是要关注网络,HTTP请求是珍贵的,特别是移动终端上,因此要使用HTTP的缓存去减少资源的加载。
Remembering these points can ensure that you have obtained most of the information in this article. J'espère que cela vous sera utile!
原文:http://coding.smashingmagazine.com/2012/11/05/writing-fast-memory-efficient-javascript/
作者:Addy Osmani