O surgimento do AJAX mudou muito o modo de operação dos clientes de aplicativos da Web. Ele permite que os usuários se concentrem em seu trabalho sem ter que suportar as irritantes atualizações de página com frequência. Em teoria, a tecnologia AJAX pode reduzir em grande medida o tempo de espera para as operações do usuário e economizar o tráfego de dados na rede. No entanto, nem sempre é esse o caso. Os usuários costumam reclamar que a velocidade de resposta dos sistemas que usam AJAX é reduzida.
O autor está envolvido em pesquisa e desenvolvimento de AJAX há muitos anos e participou do desenvolvimento do dorado, uma plataforma AJAX relativamente madura na China. De acordo com a experiência do autor, a causa raiz desse resultado não é o AJAX. Muitas vezes, a redução na velocidade de resposta do sistema é causada por um design de interface irracional e hábitos de programação insuficientemente eficientes. A seguir analisaremos vários aspectos que precisam ser observados durante o processo de desenvolvimento do AJAX.
Uso adequado de programação de cliente e chamadas de procedimento remoto.
A programação do lado do cliente é baseada principalmente em JavaScript. JavaScript é uma linguagem de programação interpretada e sua eficiência operacional é ligeiramente inferior à do Java. Ao mesmo tempo, o JavaScript é executado em um ambiente estritamente restrito, como o navegador. Portanto, os desenvolvedores devem ter um entendimento claro de qual lógica pode ser executada no lado do cliente.
A forma como a programação do lado do cliente deve ser usada em aplicativos reais depende da experiência e do julgamento do desenvolvedor. Muitos problemas aqui só podem ser compreendidos. Devido ao espaço limitado, resumimos aqui as seguintes precauções:
Evite ao máximo o uso frequente de chamadas de procedimento remoto, por exemplo, evite usar chamadas de procedimento remoto em corpos de loop.
Se possível, use chamada de procedimento remoto AJAX (chamada de procedimento remoto assíncrona).
Evite colocar operações pesadas de dados no lado do cliente. Por exemplo: grandes lotes de operações de cópia de dados, cálculos que exigem uma grande quantidade de passagem de dados, etc.
Melhorar o método de operação de objetos DOM.
Na programação do lado do cliente, as operações em objetos DOM costumam ocupar mais tempo de CPU. Para a operação de objetos DOM, a diferença de desempenho entre os diferentes métodos de programação costuma ser muito grande.
A seguir estão três trechos de código com exatamente os mesmos resultados. Sua função é criar uma tabela 10x1000 na página da web. No entanto, suas velocidades de corrida são muito diferentes.
/* Código de teste 1 - Tempo gasto: 41 segundos*/
var tabela = document.createElement("TABELA");
document.body.appendChild(tabela);
for(var i = 0; i < 1000; i++){
var linha = tabela.insertRow(-1);
for(var j = 0; j < 10; j++){
var célula = objRow.insertCell(-1);
cell.innerText = "( " + i + " , " + j + " )";
}
}
/* Código de teste 2 - Tempo gasto: 7,6 segundos*/
var tabela = document.getElementById("TABELA");
document.body.appendChild(tabela);
var tbody = document.createElement("TBODY");
tabela.appendChild(tbody);
for(var i = 0; i < 1000; i++){
var linha = document.createElement("TR");
tbody.appendChild(linha);
for(var j = 0; j < 10; j++){
var célula = document.createElement("TD");
linha.appendChild(célula);
cell.innerText = "( " + i + " , " + j + " )";
}
}
/* Código de teste 3 - tempo gasto: 1,26 segundos*/
var tbody = document.createElement("TBODY");
for(var i = 0; i < 1000; i++){
var linha = document.createElement("TR");
for(var j = 0; j < 10; j++){
var célula = document.createElement("TD");
cell.innerText = "( " + i + " , " + j + " )";
linha.appendChild(célula);
}
tbody.appendChild(linha);
}
var tabela = document.getElementById("TABELA");
tabela.appendChild(tbody);
document.body.appendChild(tabela);
A diferença entre "Código de teste 1" e "Código de teste 2" aqui é que diferentes métodos de API são usados ao criar células de tabela. A diferença entre "Código de Teste 2" e "Código de Teste 3" reside na ordem de processamento ligeiramente diferente.
Não podemos analisar uma diferença de desempenho tão grande entre "Código de teste 1" e "Código de teste 2". O que se sabe atualmente é que insertRow e insertCell são APIs específicas de tabela em DHTML, e createElement e appendChild são APIs nativas do W3C DOM. O primeiro deve ser um encapsulamento do último. No entanto, não podemos concluir disso que a API nativa do DOM seja sempre melhor que a API específica do objeto. É recomendado que você faça alguns testes básicos de desempenho quando precisar chamar uma API com frequência.
A diferença de desempenho entre "Código de Teste 2" e "Código de Teste 3" vem principalmente da diferença na ordem de construção. A abordagem do "Código de Teste 2" é primeiro criar o objeto <TABLE> mais externo e, em seguida, criar <TR> e <TD> em sequência no loop. A abordagem do "Código de Teste 3" é primeiro construir a tabela inteira na memória de dentro para fora e depois adicioná-la à página da web. O objetivo disso é reduzir ao máximo o número de vezes que o navegador recalcula o layout da página. Sempre que adicionamos um objeto a uma página web, o navegador tenta recalcular o layout dos controles na página. Portanto, se pudermos primeiro criar o objeto inteiro a ser construído na memória e depois adicioná-lo à página web de uma só vez. Então, o navegador fará apenas um recálculo do layout. Resumindo em uma frase, quanto mais tarde você executar appendChild, melhor. Às vezes, para melhorar a eficiência operacional, podemos até considerar o uso de removeChild para remover o controle existente da página e, em seguida, recolocá-lo na página após a conclusão da construção.
Melhorar a velocidade de acumulação de strings Ao usar AJAX para enviar informações, muitas vezes posso precisar montar algumas strings relativamente grandes para concluir o envio POST por meio de XmlHttp. Embora enviar uma quantidade tão grande de informações possa parecer deselegante, às vezes podemos ter que enfrentar tal necessidade. Então, quão rápido é o acúmulo de strings em JavaScript? Vamos fazer o seguinte experimento primeiro. Acumule uma string de comprimento 30.000.
/* Código de teste 1 - Tempo gasto: 14,325 segundos*/
varstr = "";
for (var i = 0; i < 50000; i++) {
str += "xxxxxx";
}
Esse código demorou 14,325 segundos e os resultados não foram os ideais. Agora alteramos o código para o seguinte formato:
/* Código de teste 2 - Tempo gasto: 0,359 segundos*/
varstr = "";
for (var i = 0; i < 100; i++) {
var sub = "";
para (var j = 0; j < 500; j++) {
sub += "xxxxxx";
}
str += sub;
}
Este código leva 0,359 segundos! Mesmo resultado, tudo o que fazemos é montar algumas cordas menores primeiro e depois montar em cordas maiores. Essa abordagem pode reduzir efetivamente a quantidade de dados copiados na memória nos estágios posteriores da montagem da string. Depois de conhecer este princípio, podemos desmontar ainda mais o código acima para teste. O código abaixo leva apenas 0,140 segundos.
/* Código de teste 3 - Tempo gasto: 0,140 segundos*/
varstr = "";
para (var i1 = 0; i1 < 5; i1++) {
varstr1 = "";
para (var i2 = 0; i2 < 10; i2++) {
varstr2 = "";
para (var i3 = 0; i3 < 10; i3++) {
varstr3 = "";
para (var i4 = 0; i4 < 10; i4++) {
varstr4 = "";
para (var i5 = 0; i5 < 10; i5++) {
str4 += "xxxxxx";
}
str3 += str4;
}
str2 += str3;
}
str1 += str2;
}
str += str1;
}
No entanto, a abordagem acima pode não ser a melhor! Se a informação que necessitamos de submeter estiver em formato XML (na verdade, na maioria dos casos, podemos tentar montar a informação a submeter em formato XML), também podemos encontrar uma forma mais eficiente e elegante - utilizando objectos DOM para montar caracteres para nós string. O parágrafo a seguir leva apenas 0,890 segundos para montar uma string com comprimento de 950015.
/* Use objetos DOM para montar informações - tempo gasto: 0,890 segundos*/
var xmlDoc;
if (browserType == BROWSER_IE) {
xmlDoc = novo ActiveXObject("Msxml.DOMDocument");
}
outro {
xmlDoc = document.createElement("DOM");
}
var raiz = xmlDoc.createElement("raiz");
for (var i = 0; i < 50000; i++) {
var node = xmlDoc.createElement("dados");
if (browserType == BROWSER_IE) {
node.text = "xxxxxx";
}
outro {
node.innerText = "xxxxxx";
}
root.appendChild(nó);
}
xmlDoc.appendChild(raiz
varstr);
if (browserType == BROWSER_IE) {
str = xmlDoc.xml;
}
outro {
str=xmlDoc.innerHTML;
}
Evite vazamentos de memória de objetos DOM.
Vazamentos de memória de objetos DOM no IE são um problema frequentemente ignorado pelos desenvolvedores. Porém, as consequências que isso traz são muito graves! Isso fará com que o uso de memória do IE continue a aumentar e que a velocidade geral de execução do navegador diminua significativamente. Para algumas páginas da web com vazamento grave, a velocidade de execução será duplicada, mesmo se atualizadas algumas vezes.
Os modelos de vazamento de memória mais comuns incluem "modelo de referência cíclica", "modelo de função de fechamento" e "modelo de pedido de inserção DOM". Para os dois primeiros modelos de vazamento, podemos evitá-los desreferenciando quando a página da web é destruída. Quanto ao “modelo de pedido de inserção DOM”, ele precisa ser evitado alterando alguns hábitos comuns de programação.
Mais informações sobre o modelo de vazamento de memória podem ser encontradas rapidamente no Google, e este artigo não irá elaborar muito. No entanto, aqui eu recomendo a você uma pequena ferramenta que pode ser usada para localizar e analisar vazamentos de memória de páginas da web - Drip. A versão mais recente atual é 0.5 e o endereço de download é http://outofhanwell.com/ieleak/index.php.
Carregamento segmentado e inicialização de páginas complexas Para algumas interfaces do sistema que são realmente complexas e inconvenientes para usar o IFrame, podemos implementar o carregamento segmentado. Por exemplo, para uma interface de guias de várias páginas, podemos primeiro baixar e inicializar a página padrão da guia de várias páginas e, em seguida, usar a tecnologia AJAH (JavaScript e HTML assíncronos) para carregar de forma assíncrona o conteúdo em outras páginas de guias. Isso garante que a interface possa ser exibida ao usuário em primeiro lugar. Dispersar o processo de carregamento de toda a interface complexa no processo de operação do usuário.
Use GZIP para compactar o tráfego de rede.
Além das melhorias no nível de código mencionadas acima, também podemos usar o GZIP para reduzir efetivamente o tráfego de rede. Atualmente, todos os navegadores convencionais já suportam o algoritmo GZIP. Muitas vezes, precisamos apenas escrever uma pequena quantidade de código para oferecer suporte ao GZIP. Por exemplo, em J2EE podemos usar o seguinte código em Filter para determinar se o navegador do cliente suporta o algoritmo GZIP e, em seguida, usar java.util.zip.GZIPOutputStream para implementar a saída GZIP conforme necessário.
/* Código para determinar como o navegador suporta GZIP*/
string estática privada getGZIPEncoding(solicitação HttpServletRequest) {
String acceptEncoding = request.getHeader("Accept-Encoding");
if (acceptEncoding == null) retornar nulo;
aceitarEncoding = aceitarEncoding.toLowerCase();
if (acceptEncoding.indexOf("x-gzip") >= 0) retornar "x-gzip";
if (acceptEncoding.indexOf("gzip") >= 0) retornar "gzip";
retornar nulo;
}
De modo geral, a taxa de compactação do GZIP para HTML e JSP pode chegar a cerca de 80%, e a perda de desempenho que causa no servidor e no cliente é quase insignificante. Combinado com outros fatores, os sites que suportam GZIP podem economizar 50% do tráfego de rede. Portanto, o uso do GZIP pode trazer melhorias significativas de desempenho para aplicações onde o ambiente de rede não é particularmente bom. Usando o Fiddler, a ferramenta de monitoramento HTTP, você pode detectar facilmente a quantidade de dados de comunicação em uma página da web antes e depois de usar o GZIP. O endereço de download do Fiddler é http://www.fiddlertool.com /fiddler/
A otimização do desempenho de aplicativos da web é, na verdade, um tópico muito importante. Devido ao espaço limitado, este artigo pode cobrir apenas alguns detalhes e também não é capaz de mostrar totalmente os métodos de otimização desses detalhes. Espero que este artigo possa chamar a atenção de todos para aplicativos da Web, especialmente para otimização de desempenho do lado do cliente. Afinal, as técnicas de programação do lado do servidor são conhecidas por todos há muitos anos e não há muito potencial para explorar o desempenho do lado do servidor. Melhorias de método no lado do cliente muitas vezes podem levar a melhorias surpreendentes de desempenho.