La aparición de AJAX ha cambiado enormemente el modo de funcionamiento de los clientes de aplicaciones web. Permite a los usuarios concentrarse en su trabajo sin tener que soportar las molestas actualizaciones de página con frecuencia. En teoría, la tecnología AJAX puede reducir en gran medida el tiempo de espera para las operaciones del usuario y ahorrar tráfico de datos en la red. Sin embargo, este no es siempre el caso. Los usuarios suelen quejarse de que se reduce la velocidad de respuesta de los sistemas que utilizan AJAX.
El autor ha estado involucrado en la investigación y el desarrollo de AJAX durante muchos años y participó en el desarrollo de Dorado, una plataforma AJAX relativamente madura en China. Según la experiencia del autor, la causa principal de este resultado no es AJAX. Muchas veces la reducción en la velocidad de respuesta del sistema se debe a un diseño de interfaz poco razonable y a hábitos de programación insuficientemente eficientes. A continuación analizaremos varios aspectos a los que se debe prestar atención durante el proceso de desarrollo de AJAX.
Uso adecuado de la programación del cliente y llamadas a procedimientos remotos.
La programación del lado del cliente se basa principalmente en JavaScript. JavaScript es un lenguaje de programación interpretado y su eficiencia operativa es ligeramente menor que la de Java. Al mismo tiempo, JavaScript se ejecuta en un entorno estrictamente restringido como el navegador. Por lo tanto, los desarrolladores deben tener una comprensión clara de qué lógica se puede ejecutar en el lado del cliente.
La forma en que se debe utilizar la programación del lado del cliente en aplicaciones reales depende de la experiencia y el criterio del desarrollador. Muchos problemas aquí sólo pueden entenderse. Debido al espacio limitado, aquí resumimos aproximadamente las siguientes precauciones:
Evite el uso frecuente de llamadas a procedimientos remotos tanto como sea posible; por ejemplo, evite el uso de llamadas a procedimientos remotos en cuerpos de bucle.
Si es posible, utilice la llamada a procedimiento remoto AJAX (llamada a procedimiento remoto asíncrona).
Evite colocar operaciones de datos pesadas en el lado del cliente. Por ejemplo: grandes lotes de operaciones de copia de datos, cálculos que requieren un gran recorrido de datos, etc.
Mejorar el método de operación de los objetos DOM.
En la programación del lado del cliente, las operaciones en objetos DOM suelen ser las que ocupan más tiempo de CPU. Para la operación de objetos DOM, la diferencia de rendimiento entre diferentes métodos de programación suele ser muy grande.
Los siguientes son tres fragmentos de código con exactamente los mismos resultados. Su función es crear una tabla de 10x1000 en la página web. Sin embargo, sus velocidades de carrera son muy diferentes.
/* Código de prueba 1 - Tiempo necesario: 41 segundos*/
var tabla = document.createElement("TABLA");
documento.body.appendChild(tabla);
para(var i = 0; i < 1000; i++){
var fila = table.insertRow(-1);
para(var j = 0; j < 10; j++){
var celda = objRow.insertCell(-1);
cell.innerText = "( " + i + " , " + j + " )";
}
}
/* Código de prueba 2 - Tiempo necesario: 7,6 segundos*/
var tabla = document.getElementById("TABLA");
documento.body.appendChild(tabla);
var tbody = document.createElement("TBODY");
tabla.appendChild(tbody);
para(var i = 0; i < 1000; i++){
var fila = document.createElement("TR");
tbody.appendChild(fila);
para(var j = 0; j < 10; j++){
var celda = document.createElement("TD");
fila.appendChild(celda);
cell.innerText = "( " + i + " , " + j + " )";
}
}
/* Código de prueba 3 - tiempo necesario: 1,26 segundos*/
var tbody = document.createElement("TBODY");
para(var i = 0; i < 1000; i++){
var fila = document.createElement("TR");
para(var j = 0; j < 10; j++){
var celda = document.createElement("TD");
cell.innerText = "( " + i + " , " + j + " )";
fila.appendChild(celda);
}
tbody.appendChild(fila);
}
var tabla = document.getElementById("TABLA");
tabla.appendChild(tbody);
documento.body.appendChild(tabla);
La diferencia entre el "Código de prueba 1" y el "Código de prueba 2" aquí es que se utilizan diferentes métodos API al crear celdas de tabla. La diferencia entre el "Código de prueba 2" y el "Código de prueba 3" radica en el orden de procesamiento ligeramente diferente.
No podemos analizar una diferencia de rendimiento tan grande entre el "Código de prueba 1" y el "Código de prueba 2". Lo que se sabe actualmente es que insertRow e insertCell son API específicas de tablas en DHTML, y createElement y appendChild son API nativas de W3C DOM. El primero debería ser una encapsulación del segundo. Sin embargo, no podemos concluir de esto que la API nativa del DOM sea siempre mejor que la API específica de objeto. Se recomienda realizar algunas pruebas básicas sobre su rendimiento cuando necesite llamar a una API con frecuencia.
La diferencia de rendimiento entre el "Código de prueba 2" y el "Código de prueba 3" proviene principalmente de la diferencia en su orden de compilación. El enfoque del "Código de prueba 2" es crear primero el objeto <TABLE> más externo y luego crear <TR> y <TD> en secuencia en el bucle. El enfoque del "Código de prueba 3" es primero construir la tabla completa en la memoria desde adentro hacia afuera y luego agregarla a la página web. El propósito de esto es reducir al máximo la cantidad de veces que el navegador recalcula el diseño de la página. Cada vez que agregamos un objeto a una página web, el navegador intenta recalcular el diseño de los controles en la página. Por lo tanto, si primero podemos crear el objeto completo que se construirá en la memoria y luego agregarlo a la página web de una vez. Luego, el navegador solo volverá a calcular el diseño. Para resumirlo en una oración, cuanto más tarde ejecute appendChild, mejor. A veces, para mejorar la eficiencia operativa, incluso podemos considerar usar removeChild para eliminar el control existente de la página y luego volver a colocarlo en la página una vez que se complete la construcción.
Mejorar la velocidad de acumulación de cadenas Cuando uso AJAX para enviar información, es posible que a menudo necesite ensamblar algunas cadenas relativamente grandes para completar el envío POST a través de XmlHttp. Aunque enviar una cantidad tan grande de información pueda parecer poco elegante, a veces podemos tener que afrontar esa necesidad. Entonces, ¿qué tan rápida es la acumulación de cadenas en JavaScript? Hagamos primero el siguiente experimento. Acumule una cadena de longitud 30000.
/* Código de prueba 1 - Tiempo necesario: 14,325 segundos*/
var cadena = "";
para (var i = 0; i < 50000; i++) {
cadena += "xxxxxx";
}
Este código tardó 14,325 segundos y los resultados no fueron los ideales. Ahora cambiamos el código a la siguiente forma:
/* Código de prueba 2 - Tiempo necesario: 0,359 segundos*/
var cadena = "";
para (var i = 0; i < 100; i++) {
var sub = "";
para (var j = 0; j < 500; j++) {
sub += "xxxxxx";
}
cadena += sub;
}
¡Este código tarda 0,359 segundos! El mismo resultado, todo lo que hacemos es ensamblar algunas cadenas más pequeñas primero y luego ensamblar cadenas más grandes. Este enfoque puede reducir efectivamente la cantidad de datos copiados en la memoria en las últimas etapas del ensamblaje de cadenas. Después de conocer este principio, podemos desmantelar aún más el código anterior para realizar pruebas. El siguiente código solo toma 0,140 segundos.
/* Código de prueba 3 - Tiempo necesario: 0,140 segundos*/
var cadena = "";
para (var i1 = 0; i1 < 5; i1++) {
var cadena1 = "";
para (var i2 = 0; i2 < 10; i2++) {
var cadena2 = "";
para (var i3 = 0; i3 < 10; i3++) {
var cadena3 = "";
para (var i4 = 0; i4 < 10; i4++) {
var cadena4 = "";
para (var i5 = 0; i5 < 10; i5++) {
cadena4 += "xxxxxx";
}
cadena3 += cadena4;
}
cadena2 += cadena3;
}
cadena1 += cadena2;
}
cadena += cadena1;
}
Sin embargo, ¡el enfoque anterior puede no ser el mejor! Si la información que necesitamos enviar está en formato XML (de hecho, en la mayoría de los casos, podemos intentar ensamblar la información que se enviará en formato XML), también podemos encontrar una forma más eficiente y elegante: usar objetos DOM para ensamblar. caracteres para nosotros cadena. El siguiente párrafo solo toma 0,890 segundos para ensamblar una cuerda con una longitud de 950015.
/* Utiliza objetos DOM para reunir información - tiempo necesario: 0,890 segundos*/
var xmlDoc;
si (tipo de navegador == NAVEGADOR_IE) {
xmlDoc = nuevo ActiveXObject("Msxml.DOMDocument");
}
demás {
xmlDoc = documento.createElement("DOM");
}
var raíz = xmlDoc.createElement("raíz");
para (var i = 0; i < 50000; i++) {
var nodo = xmlDoc.createElement("datos");
si (tipo de navegador == NAVEGADOR_IE) {
nodo.text = "xxxxxx";
}
demás {
nodo.innerText = "xxxxxx";
}
root.appendChild(nodo);
}
xmlDoc.appendChild(raíz);
var str;
si (tipo de navegador == NAVEGADOR_IE) {
cadena = xmlDoc.xml;
}
demás {
cadena = xmlDoc.innerHTML;
}
Evite pérdidas de memoria de objetos DOM.
Las pérdidas de memoria de los objetos DOM en IE son un problema que los desarrolladores suelen ignorar. Sin embargo, ¡las consecuencias que trae son muy graves! Hará que el uso de memoria de IE siga aumentando y que la velocidad de ejecución general del navegador se reduzca significativamente. Para algunas páginas web con filtraciones graves, la velocidad de ejecución se duplicará incluso si se actualizan varias veces.
Los modelos de pérdida de memoria más comunes incluyen el "modelo de referencia cíclico", el "modelo de función de cierre" y el "modelo de orden de inserción DOM". Para los dos primeros modelos de pérdida, podemos evitarlos desreferenciando cuando se destruye la página web. En cuanto al "modelo de orden de inserción DOM", es necesario evitarlo cambiando algunos hábitos de programación comunes.
Puede encontrar rápidamente más información sobre el modelo de pérdida de memoria a través de Google, y este artículo no se extenderá demasiado. Sin embargo, aquí le recomiendo una pequeña herramienta que puede usarse para encontrar y analizar pérdidas de memoria en páginas web: Drip. La versión más reciente es 0.5 y la dirección de descarga es http://outofhanwell.com/ieleak/index.php.
Carga segmentada e inicialización de páginas complejas. Para algunas interfaces en el sistema que son realmente complejas e inconvenientes para usar IFrame, podemos implementar la carga segmentada. Por ejemplo, para una interfaz de pestañas de varias páginas, primero podemos descargar e inicializar la página predeterminada de la pestaña de varias páginas y luego usar la tecnología AJAH (JavaScript y HTML asíncronos) para cargar de forma asincrónica el contenido en otras páginas de pestañas. Esto garantiza que la interfaz se pueda mostrar al usuario en primer lugar. Dispersa el proceso de carga de toda la interfaz compleja en el proceso de operación del usuario.
Utilice GZIP para comprimir el tráfico de red.
Además de las mejoras a nivel de código mencionadas anteriormente, también podemos usar GZIP para reducir efectivamente el tráfico de la red. En la actualidad, todos los navegadores convencionales ya admiten el algoritmo GZIP. A menudo, solo necesitamos escribir una pequeña cantidad de código para admitir GZIP. Por ejemplo, en J2EE podemos usar el siguiente código en Filtro para determinar si el navegador del cliente admite el algoritmo GZIP y luego usar java.util.zip.GZIPoutputStream para implementar la salida GZIP según sea necesario.
/* Código para determinar cómo el navegador soporta GZIP*/
Cadena estática privada getGZIPEncoding (solicitud HttpServletRequest) {
String aceptarEncodificación = request.getHeader("Aceptar-Codificación");
si (acceptEncoding == null) devuelve nulo;
aceptarEncodificación = aceptarEncodificación.toLowerCase();
si (acceptEncoding.indexOf("x-gzip") >= 0) devuelve "x-gzip";
si (acceptEncoding.indexOf("gzip") >= 0) devuelve "gzip";
devolver nulo;
}
En términos generales, la relación de compresión de GZIP para HTML y JSP puede alcanzar aproximadamente el 80% y la pérdida de rendimiento que provoca en el servidor y el cliente es casi insignificante. Combinado con otros factores, los sitios web que admiten GZIP pueden ahorrarnos el 50% del tráfico de la red. Por tanto, el uso de GZIP puede aportar importantes mejoras de rendimiento a aplicaciones donde el entorno de red no es especialmente bueno. Con Fiddler, la herramienta de monitoreo HTTP, puede detectar fácilmente la cantidad de datos de comunicación en una página web antes y después de usar GZIP. La dirección de descarga de Fiddler es http://www.fiddlertool.com /fiddler/
La optimización del rendimiento de las aplicaciones web es en realidad un tema muy importante. Debido al espacio limitado, este artículo solo puede cubrir algunos de los detalles y tampoco puede mostrarle completamente los métodos de optimización de estos detalles. Espero que este artículo pueda llamar la atención de todos sobre las aplicaciones web, especialmente la optimización del rendimiento del lado del cliente. Después de todo, las técnicas de programación del lado del servidor son conocidas por todos desde hace muchos años y no hay mucho potencial para explotar el rendimiento en el lado del servidor. Las mejoras en los métodos en el lado del cliente a menudo pueden conducir a mejoras sorprendentes en el rendimiento.