L'émergence d'AJAX a considérablement modifié le mode de fonctionnement des clients d'applications Web. Il permet aux utilisateurs de se concentrer sur leur travail sans avoir à supporter les actualisations fastidieuses des pages. En théorie, la technologie AJAX peut réduire considérablement le temps d'attente pour les opérations des utilisateurs et économiser le trafic de données sur le réseau. Cependant, ce n’est pas toujours le cas. Les utilisateurs se plaignent souvent du fait que la vitesse de réponse des systèmes utilisant AJAX est réduite.
L'auteur est engagé dans la recherche et le développement d'AJAX depuis de nombreuses années et a participé au développement de dorado, une plate-forme AJAX relativement mature en Chine. D'après l'expérience de l'auteur, la cause première de ce résultat n'est pas AJAX. Souvent, la réduction de la vitesse de réponse du système est causée par une conception d’interface déraisonnable et des habitudes de programmation insuffisamment efficaces. Ci-dessous, nous analyserons plusieurs aspects auxquels il faut prêter attention lors du processus de développement AJAX.
Bonne utilisation de la programmation client et des appels de procédures à distance.
La programmation côté client est principalement basée sur JavaScript. JavaScript est un langage de programmation interprété et son efficacité opérationnelle est légèrement inférieure à celle de Java. Dans le même temps, JavaScript s’exécute dans un environnement strictement restreint tel que le navigateur. Par conséquent, les développeurs doivent avoir une compréhension claire de la logique qui peut être exécutée côté client.
La manière dont la programmation côté client doit être utilisée dans les applications réelles dépend de l'expérience et du jugement du développeur. De nombreux problèmes ici ne peuvent qu’être compris. En raison de l'espace limité, nous résumons ici grossièrement les précautions suivantes :
Évitez autant que possible l'utilisation fréquente d'appels de procédure distante, par exemple, évitez d'utiliser des appels de procédure distante dans des corps de boucle.
Si possible, utilisez l'appel de procédure distante AJAX (appel de procédure distante asynchrone).
Évitez de placer des opérations de données lourdes du côté client. Par exemple : de grands lots d'opérations de copie de données, des calculs nécessitant une grande quantité de parcours de données, etc.
Améliorer la méthode de fonctionnement des objets DOM.
Dans la programmation côté client, les opérations sur les objets DOM sont souvent les plus susceptibles d'occuper du temps CPU. Pour le fonctionnement des objets DOM, la différence de performances entre les différentes méthodes de programmation est souvent très importante.
Voici trois morceaux de code avec exactement les mêmes résultats. Leur fonction est de créer un tableau 10x1000 dans la page Web. Cependant, leurs vitesses de course sont très différentes.
/* Code de test 1 - Temps pris : 41 secondes*/
var table = document.createElement("TABLE");
document.body.appendChild(table);
pour(var je = 0; je < 1000; je++){
var ligne = table.insertRow(-1);
pour(var j = 0; j < 10; j++){
var cellule = objRow.insertCell(-1);
cell.innerText = "( " + i + " , " + j + " )";
}
}
/* Code de test 2 - Temps pris : 7,6 secondes*/
var table = document.getElementById("TABLE");
document.body.appendChild(table);
var tbody = document.createElement("TBODY");
table.appendChild(tbody);
pour(var je = 0; je < 1000; je++){
var row = document.createElement("TR");
tbody.appendChild(ligne);
pour(var j = 0; j < 10; j++){
var cell = document.createElement("TD");
rangée.appendChild(cellule);
cell.innerText = "( " + i + " , " + j + " )";
}
}
/* Code de test 3 - temps pris : 1,26 seconde*/
var tbody = document.createElement("TBODY");
pour(var je = 0; je < 1000; je++){
var row = document.createElement("TR");
pour(var j = 0; j < 10; j++){
var cell = document.createElement("TD");
cell.innerText = "( " + i + " , " + j + " )";
rangée.appendChild(cellule);
}
tbody.appendChild(ligne);
}
var table = document.getElementById("TABLE");
table.appendChild(tbody);
document.body.appendChild(table);
La différence entre « Test Code 1 » et « Test Code 2 » ici est que différentes méthodes API sont utilisées lors de la création de cellules de tableau. La différence entre le « Code de test 2 » et le « Code de test 3 » réside dans l'ordre de traitement légèrement différent.
Nous ne pouvons pas analyser une si grande différence de performances entre "Test Code 1" et "Test Code 2". Ce que l'on sait actuellement, c'est que insertRow et insertCell sont des API spécifiques aux tables en DHTML, et createElement et appendChild sont des API natives du DOM du W3C. Le premier devrait être une encapsulation du second. Cependant, nous ne pouvons pas en conclure que l'API native du DOM est toujours meilleure que l'API spécifique aux objets. Il est recommandé d'effectuer quelques tests de base sur ses performances lorsque vous devez appeler fréquemment une API.
La différence de performances entre "Test Code 2" et "Test Code 3" vient principalement de la différence dans leur ordre de construction. L'approche du « Code de test 2 » consiste à créer d'abord l'objet <TABLE> le plus externe, puis à créer <TR> et <TD> en séquence dans la boucle. L'approche du "Test Code 3" consiste d'abord à créer la table entière en mémoire de l'intérieur vers l'extérieur, puis à l'ajouter à la page Web. Le but est de réduire autant que possible le nombre de fois où le navigateur recalcule la mise en page. Chaque fois que nous ajoutons un objet à une page Web, le navigateur tente de recalculer la disposition des contrôles sur la page. Par conséquent, si nous pouvons d’abord créer l’objet entier à construire en mémoire, puis l’ajouter immédiatement à la page Web. Ensuite, le navigateur effectuera uniquement un recalcul de mise en page. Pour résumer en une phrase, plus vous exécutez appendChild tard, mieux c'est. Parfois, afin d'améliorer l'efficacité opérationnelle, nous pouvons même envisager d'utiliser RemoveChild pour supprimer le contrôle existant de la page, puis de le replacer sur la page une fois la construction terminée.
Améliorer la vitesse d'accumulation de chaînes Lorsque j'utilise AJAX pour soumettre des informations, je peux souvent avoir besoin d'assembler des chaînes relativement volumineuses pour terminer la soumission POST via XmlHttp. Bien que soumettre une telle quantité d’informations puisse sembler inélégant, nous pouvons parfois être confrontés à un tel besoin. Alors, à quelle vitesse l’accumulation de chaînes en JavaScript est-elle ? Faisons d’abord l’expérience suivante. Accumulez une chaîne de longueur 30 000.
/* Code de test 1 - Temps pris : 14,325 secondes*/
var str = "";
pour (var je = 0; je < 50000; je++) {
str += "xxxxxx";
}
Ce code a pris 14,325 secondes et les résultats n'étaient pas idéaux. Maintenant, nous modifions le code sous la forme suivante :
/* Code de test 2 - Temps pris : 0,359 seconde*/
var str = "";
pour (var je = 0; je < 100; je++) {
var sous = "";
pour (var j = 0; j < 500; j++) {
sous += "xxxxxx" ;
}
str += sous;
}
Ce code prend 0,359 seconde ! Même résultat, tout ce que nous faisons est d’assembler d’abord quelques chaînes plus petites, puis de les assembler en chaînes plus grandes. Cette approche peut réduire efficacement la quantité de données copiées en mémoire lors des étapes ultérieures de l'assemblage de chaînes. Après avoir connu ce principe, nous pouvons démonter davantage le code ci-dessus à des fins de test. Le code ci-dessous ne prend que 0,140 seconde.
/* Code de test 3 - Temps pris : 0,140 secondes*/
var str = "";
pour (var i1 = 0; i1 < 5; i1++) {
var str1 = "";
pour (var i2 = 0; i2 < 10; i2++) {
var str2 = "";
pour (var i3 = 0; i3 < 10; i3++) {
var str3 = "";
pour (var i4 = 0; i4 < 10; i4++) {
var str4 = "";
pour (var i5 = 0 ; i5 < 10 ; i5++) {
str4 += "xxxxxx" ;
}
str3 += str4;
}
chaîne2 += chaîne3;
}
chaîne1 += chaîne2;
}
str += str1;
}
Cependant, l’approche ci-dessus n’est peut-être pas la meilleure ! Si les informations que nous devons soumettre sont au format XML (en fait, dans la plupart des cas, nous pouvons essayer de rassembler les informations à soumettre au format XML), nous pouvons également trouver une manière plus efficace et plus élégante : utiliser des objets DOM pour assembler caractères pour nous chaîne. Le paragraphe suivant ne prend que 0,890 seconde pour assembler une chaîne d'une longueur de 950015.
/* Utiliser des objets DOM pour assembler des informations - temps nécessaire : 0,890 seconde*/
var xmlDoc;
si (browserType == BROWSER_IE) {
xmlDoc = new ActiveXObject("Msxml.DOMDocument");
}
autre {
xmlDoc = document.createElement("DOM");
}
var racine = xmlDoc.createElement("racine");
pour (var je = 0; je < 50000; je++) {
var node = xmlDoc.createElement("données");
si (browserType == BROWSER_IE) {
node.text = "xxxxxx" ;
}
autre {
node.innerText = "xxxxxx" ;
}
root.appendChild(nœud);
}
xmlDoc.appendChild(racine);
var str;
si (browserType == BROWSER_IE) {
str = xmlDoc.xml;
}
autre {
str = xmlDoc.innerHTML ;
}
Évitez les fuites de mémoire des objets DOM.
Les fuites de mémoire des objets DOM dans IE sont un problème souvent ignoré par les développeurs. Cependant, les conséquences que cela entraîne sont très graves ! Cela entraînera une augmentation continue de l'utilisation de la mémoire d'IE et un ralentissement significatif de la vitesse d'exécution globale du navigateur. Pour certaines pages Web ayant fait l'objet de fuites graves, la vitesse d'exécution sera doublée même si elles sont actualisées plusieurs fois.
Les modèles de fuite de mémoire les plus courants incluent le « modèle de référence cyclique », le « modèle de fonction de fermeture » et le « modèle d'ordre d'insertion DOM ». Pour les deux premiers modèles de fuite, nous pouvons les éviter en déréférençant lorsque la page Web est détruite. Quant au "modèle d'ordre d'insertion DOM", il doit être évité en modifiant certaines habitudes de programmation courantes.
Plus d’informations sur le modèle de fuite de mémoire peuvent être trouvées rapidement via Google, et cet article ne s’étendra pas trop. Cependant, je vous recommande ici un petit outil qui peut être utilisé pour rechercher et analyser les fuites de mémoire des pages Web - Drip. La version la plus récente est la 0.5 et l'adresse de téléchargement est http://outofhanwell.com/ieleak/index.php.
Chargement segmenté et initialisation de pages complexes Pour certaines interfaces du système qui sont vraiment complexes et peu pratiques à utiliser IFrame, nous pouvons implémenter un chargement segmenté. Par exemple, pour une interface à onglets multipages, nous pouvons d'abord télécharger et initialiser la page par défaut de l'onglet multipage, puis utiliser la technologie AJAH (JavaScript et HTML asynchrones) pour charger de manière asynchrone le contenu dans d'autres pages à onglets. Cela garantit que l'interface peut être affichée à l'utilisateur en premier lieu. Dispersez le processus de chargement de l’ensemble de l’interface complexe dans le processus opérationnel de l’utilisateur.
Utilisez GZIP pour compresser le trafic réseau.
En plus des améliorations au niveau du code mentionnées ci-dessus, nous pouvons également utiliser GZIP pour réduire efficacement le trafic réseau. À l'heure actuelle, tous les navigateurs grand public prennent déjà en charge l'algorithme GZIP. Il suffit souvent d'écrire une petite quantité de code pour prendre en charge GZIP. Par exemple, dans J2EE, nous pouvons utiliser le code suivant dans Filter pour déterminer si le navigateur client prend en charge l'algorithme GZIP, puis utiliser java.util.zip.GZIPoutputStream pour implémenter la sortie GZIP si nécessaire.
/* Code pour déterminer comment le navigateur prend en charge GZIP*/
chaîne statique privée getGZIPEncoding (requête HttpServletRequest) {
String acceptEncoding = request.getHeader("Accept-Encoding");
if (acceptEncoding == null) renvoie null ;
acceptEncoding = acceptEncoding.toLowerCase();
if (acceptEncoding.indexOf("x-gzip") >= 0) return "x-gzip" ;
if (acceptEncoding.indexOf("gzip") >= 0) return "gzip" ;
renvoie null ;
}
D'une manière générale, le taux de compression de GZIP pour HTML et JSP peut atteindre environ 80 %, et la perte de performances qu'il entraîne sur le serveur et le client est presque négligeable. Combinés à d’autres facteurs, les sites Web prenant en charge GZIP peuvent nous faire économiser 50 % du trafic réseau. Par conséquent, l’utilisation de GZIP peut apporter des améliorations significatives des performances aux applications où l’environnement réseau n’est pas particulièrement bon. Grâce à Fiddler, l'outil de surveillance HTTP, vous pouvez facilement détecter la quantité de données de communication sur une page Web avant et après l'utilisation de GZIP. L'adresse de téléchargement de Fiddler est http://www.fiddlertool.com /fiddler/
L’optimisation des performances des applications Web est en réalité un sujet très vaste. En raison de l'espace limité, cet article ne peut couvrir que quelques détails, et il n'est pas non plus en mesure de vous montrer entièrement les méthodes d'optimisation de ces détails. J'espère que cet article pourra attirer l'attention de tous sur les applications Web, en particulier sur l'optimisation des performances côté client. Après tout, les techniques de programmation côté serveur sont connues de tous depuis de nombreuses années et il n'y a pas beaucoup de potentiel pour exploiter les performances côté serveur. Les améliorations de méthodes côté client peuvent souvent conduire à des améliorations surprenantes des performances.