Como criar programas de front-end da Web eficientes é um problema que considero inconscientemente toda vez que faço o desenvolvimento do front-end. Alguns anos atrás, os incríveis engenheiros de front-end do Yahoo publicaram um livro sobre como melhorar o desempenho do front-end da Web, o que causou uma sensação em todo o setor de tecnologia de desenvolvimento da Web, tornando a misteriosa pergunta de otimização de front-end da Web um repolho na rua, e a otimização do front-end da Web se tornou uma pergunta simples que os novatos e os grandes gigantes podem responder. Quando todo o setor sabe a resposta para o segredo chocante, a tecnologia de otimização existente não pode mais produzir um salto qualitativo para o site que você desenvolve. Para tornar o desempenho do site, desenvolvemos melhor do que os sites de outras pessoas, precisamos pensar mais profundamente e reservar melhores habilidades.
O sistema de eventos em JavaScript é o primeiro ponto de avanço que eu penso. Por que é um sistema de eventos JavaScript? Todos sabemos que o front-end da Web contém três tecnologias: HTML, CSS e JavaScript. É claro como o HTML e o CSS são combinados: etiquetas de estilo, classe, ID e html. Não há nada para falar, mas como o JavaScript entra no meio entre HTML e CSS e permite que os três se integrem? Finalmente, descobri que esse ponto de entrada é o sistema de eventos de JavaScript. Não importa quanto tempo ou o código JavaScript complexo que escrevemos, ele se reflete em HTML e CSS através do sistema de eventos. Portanto, eu estava pensando que, como o sistema de eventos é o ponto de entrada para a integração dos três, haverá inevitavelmente um grande número de operações de eventos em uma página, especialmente nas páginas da web cada vez mais complexas de hoje. Sem esses eventos, o código JavaScript que escrevemos com cuidado só pode ser usado na biblioteca e os heróis são inúteis. Como há um grande número de funções de evento na página, haverá problemas que afetam a eficiência ao escrever funções de eventos como estamos acostumados? A resposta que estudei é que é um problema real de eficiência e também é um sério problema de eficiência.
Para esclarecer minha resposta, quero explicar o sistema de eventos de JavaScript em detalhes.
O sistema de eventos é o ponto de entrada para a fusão de JavaScript, HTML e CSS. Este ponto é como a principal função em Java. Toda a magia começa aqui. Então, como o navegador completa esta entrada? Eu estudei três maneiras, elas são:
Método 1: processamento de eventos HTML
O processamento de eventos HTML é escrever funções de evento diretamente na tag HTML. Como este método de escrita está intimamente associado à tag HTML, ele é chamado de processamento de eventos HTML. Por exemplo, o seguinte código:
A cópia do código é a seguinte:
<input type = "button" id = "btn" name = "btn" onclick = "alert ('clique em mim!')"/>
Se a função de evento de clique for complicada, escrever código como esse definitivamente causará inconvenientes. Portanto, frequentemente escrevemos a função fora e OnClick chamamos diretamente o nome da função, por exemplo:
A cópia do código é a seguinte:
<input type = "button" id = "btn" name = "btn" onclick = "btnclk ()"/>
função btnclk () {
alerta ("Clique em mim!");
}
O método de escrita acima é um método de escrita muito bonito, tantas pessoas inconscientemente o usam hoje em dia, mas muitas pessoas podem não saber que o último método de escrita não é tão forte quanto o método de escrita anterior. Esse também é um problema que encontrei ao estudar a tecnologia de script de carregamento sem bloqueio há pouco tempo, porque, de acordo com o princípio da otimização do front-end, o código JavaScript está frequentemente localizado na parte inferior da página. Quando a página é bloqueada por um script, a função referenciada na tag HTML pode ainda não ter sido executada. No momento, clicamos no botão da página e o resultado será relatado "a função XXX é um erro indefinido". No JavaScript, esses erros serão pegos por tentar e pegar. Portanto, para tornar o código mais robusto, teremos as seguintes reescritas:
A cópia do código é a seguinte:
<input type = "button" id = "btn" name = "btn" onclick = "try {btnclk ();} catch (e) {}"/>
Ver o código acima não é algo que pode ser descrito por uma pessoa nojenta.
Método 2: Processamento de eventos de nível DOM0
O processamento de eventos de nível DOM0 é um processamento de eventos suportado por todos os navegadores hoje. Não há problema de compatibilidade. Ver essa frase deixará todos que fizeram um front-end da Web empolgado. A regra para o processamento de eventos DOM0 é: Cada elemento DOM possui seu próprio atributo de processamento de eventos, que pode receber uma função, como o seguinte código:
A cópia do código é a seguinte:
var btnDom = document.getElementById ("btn");
btnDom.OnClick = function () {
alerta ("Clique em mim!");
}
Os atributos do evento tratados pelos eventos do nível DOM0 são definidos na forma de "On+Nome do Evento", e todo o atributo está em letras minúsculas. Sabemos que o elemento DOM é um objeto JavaScript no código JavaScript, por isso é muito fácil entender o processamento de eventos no nível do DOM0 da perspectiva do objeto JavaScript, por exemplo, o seguinte código:
A cópia do código é a seguinte:
btnDom.OnClick = null;
Em seguida, o evento de clique no botão é cancelado.
Vejamos o seguinte código:
A cópia do código é a seguinte:
btnDom.OnClick = function () {
alerta ("Clique em mim!");
}
btnDom.OnClick = function () {
alerta ("Clique em ME1111!");
}
A próxima função substituirá a primeira função.
Método 3: Processamento de eventos DOM2 e processamento de eventos IE
O Processamento de Eventos DOM2 é uma solução padronizada de processamento de eventos, mas o IE navegador desenvolveu um conjunto de funções e funções é semelhante ao processamento de eventos DOM2, mas o código é diferente disso.
Antes de explicar o método três, devo adicionar alguns conceitos; caso contrário, não poderei explicar a conotação do método três.
O primeiro conceito é: fluxo de eventos
No desenvolvimento de páginas, geralmente encontramos essa situação. O intervalo de trabalho de uma página pode ser representado por um documento no JavaScript. Há uma div na página. A div é equivalente a sobrepor o elemento do documento. Há um elemento de botão no div. O elemento do botão está sobrepondo a div, que também é equivalente a sobrepor o documento. Portanto, o problema é que, quando clicamos neste botão, esse comportamento de clique não só acontece no botão. Tanto o DIV quanto o documento são usados para operações de cliques. De acordo com a lógica, esses três elementos podem desencadear eventos de clique. O fluxo de eventos descreve o conceito do cenário acima. O fluxo de eventos significa: a ordem dos eventos recebidos da página.
O segundo conceito: bolhas de eventos e captura de eventos
As bolhas de eventos são a solução proposta pela Microsoft para resolver o problema de fluxo de eventos, enquanto a captura de eventos é a solução de fluxo de eventos proposta pelo Netscape. Seus princípios são os seguintes:
O evento borbulhante começa com uma div, seguido por um corpo e, finalmente, um documento, e a captura do evento é revertida, primeiro um documento, seguido por um corpo e, finalmente, um DIV de elemento alvo. Por outro lado, a solução da Microsoft é mais humana e, de acordo com os hábitos operacionais das pessoas, a solução do Netscape é muito estranha. Esta é a conseqüência da guerra do navegador. O Netscape é mais lento e resolve o problema do fluxo de eventos sacrificando o código com o qual os usuários estão acostumados.
A Microsoft projetou um novo sistema de eventos em combinação com eventos de bolhas, que é comumente conhecida como processamento de eventos do IE no setor. O método de processamento de eventos do IE é como mostrado no código a seguir:
A cópia do código é a seguinte:
var btnDom = document.getElementById ("btn");
btndom.attachevent ("OnClick", function () {
alerta ("Clique em mim!");
});
Adicione eventos através do método APLENTEVENT do elemento DOM no IE. Comparado com o processamento de eventos DOM0, o método de adição de eventos mudou de atributos para métodos; portanto, quando adicionamos eventos, precisamos passar os parâmetros para o método. O método ApptlementEvent recebe dois parâmetros. O primeiro parâmetro é o tipo de evento. A nomeação do tipo de evento é a mesma que a nomeação do evento no processamento do evento DOM0. O segundo parâmetro é a função de evento. A vantagem de usar o método é que, se estivermos adicionando um evento de clique ao mesmo elemento, como mostrado abaixo:
A cópia do código é a seguinte:
btndom.attachevent ("OnClick", function () {
alerta ("Clique em mim!");
});
btndom.attachevent ("OnClick", function () {
alerta ("Clique em mim também!");
});
Execute -o, ambas as caixas de diálogo aparecem normalmente, permitindo -nos adicionar vários eventos de cliques diferentes ao elemento DOM. E se não quisermos um evento? O que devemos fazer? Eu forneço um método de destaque para excluir eventos. A lista de parâmetros é a mesma que o AnexeEvent. Se quisermos excluir um determinado evento de clique, basta passar os mesmos parâmetros do evento Add, conforme mostrado no código a seguir:
A cópia do código é a seguinte:
btndom.detachevent ("OnClick", function () {
alerta ("Clique em mim também!");
});
Ao executá -lo, as consequências são muito sérias e estamos confusos. O segundo clique não foi excluído. O que está acontecendo? Mencionei anteriormente que a exclusão de eventos requer parâmetros de passagem, como adicionar eventos. No entanto, na função anônima do JavaScript, mesmo que o código das duas funções anônimas seja exatamente o mesmo, o JavaScript usará variáveis diferentes para armazená -las internamente. Como resultado, o fenômeno que vemos não pode excluir o evento de clique, portanto nosso código deve ser escrito assim:
A cópia do código é a seguinte:
var ftn = function () {
alerta ("Clique em mim também!");
};
btndom.attachevent ("OnClick", ftn);
btnDom.Detachevent ("OnClick", ftn);
O método adicionado e o método excluído dessa maneira apontam para o mesmo objeto; portanto, o evento excluiu com sucesso. O cenário aqui nos diz que precisamos ter um bom hábito de escrever eventos que devemos definir operações de forma independente e não usar funções anônimas como um hábito.
Em seguida, o processamento de eventos DOM2, seu princípio é mostrado na figura abaixo:
DOM2 é um evento padronizado. Usando eventos DOM2, a entrega de eventos começa no método de captura, ou seja, do documento e depois ao corpo. A div é um ponto intermediário. Quando o evento atinge o ponto intermediário, o evento está no estágio de destino. Depois que o evento entra no estágio de destino, o evento começa a borbulhar e manipular e, finalmente, o evento termina no documento. (Estou apontando para documentar neste artigo, mas a situação real é que alguns navegadores começarão a capturar o evento e terminar a bolha na janela. No entanto, acho que, não importa como o próprio navegador seja definido durante o desenvolvimento, é mais orientado para o desenvolvimento para prestar atenção para documentar, por isso estou usando o documento aqui). As pessoas são usadas para classificar o estágio -alvo como parte da bolha, principalmente porque os eventos de bolha são mais amplamente utilizados no desenvolvimento.
O processamento de eventos DOM2 é muito difícil. Quando cada evento for acionado, todos os elementos serão percorridos duas vezes. Comparado com o evento do IE, o desempenho é muito pior que o evento do IE. Eu só borbulhava, então só preciso atravessar uma vez. No entanto, menos travessia não significa que o sistema de eventos do IE seja mais eficiente. O suporte a dois sistemas de eventos ao mesmo tempo trará maior flexibilidade ao nosso desenvolvimento da perspectiva de desenvolvimento e design. Nessa perspectiva, os eventos DOM2 ainda são muito desejáveis. O código para o evento DOM2 é o seguinte:
A cópia do código é a seguinte:
var btnDom = document.getElementById ("btn");
btndom.addeventListener ("clique", function () {
alerta ("Clique em mim!");
},falso);
var ftn = function () {
alerta ("Clique em mim também!");
};
btndom.addeventListener ("clique", ftn, false);
A adição de eventos no processamento de eventos DOM2 usa o addEventListener, que recebe mais um parâmetro que o processamento de eventos do IE. Os dois primeiros significam o mesmo que os dois parâmetros do método de processamento de eventos do IE. A única diferença é que o prefixo está removido no primeiro parâmetro e o terceiro parâmetro é um valor booleano. Se seu valor for verdadeiro, o evento será processado no modo de captura, com o valor false. O evento é processado em bolhas. Com o terceiro parâmetro, podemos entender por que o elemento de evento deve ser executado duas vezes no processamento de eventos DOM2. O objetivo é ser compatível com os dois modelos de eventos. No entanto, observe aqui que, não importa se optarmos por capturar ou borbulhar, as duas travessias serão realizadas para sempre. Se escolhermos um método de processamento de eventos, nenhuma função de processamento de eventos será acionada no outro processo de processamento de eventos. É o mesmo que o princípio de marcha lenta no carro em engrenagem neutra. Através do design do método do evento DOM2, sabemos que os eventos DOM2 podem executar apenas um dos dois métodos de processamento de eventos em tempo de execução. É impossível para dois sistemas de fluxo de eventos provocarem ao mesmo tempo. Portanto, embora o elemento seja percorrido duas vezes, a função de evento não pode ser provocada duas vezes. Observe que quero dizer que não foi provocada duas vezes, o que se refere a uma função de evento. De fato, podemos simular a situação em que dois modelos de fluxo de eventos são executados ao mesmo tempo, como o seguinte código:
A cópia do código é a seguinte:
btndom.addeventListener ("clique", ftn, true);
btndom.addeventListener ("clique", ftn, false);
Mas essa maneira de escrever é o processamento de vários eventos, o que equivale a clicar no botão duas vezes.
O DOM2 também fornece uma função para excluir eventos. Esta função é removida, escrita da seguinte forma:
A cópia do código é a seguinte:
btndom.RemoveEventListener ("clique", ftn, false);
O mesmo vale para o evento do IE, ou seja, os parâmetros devem ser consistentes com os parâmetros que definem o evento. No entanto, ao usar o RemoneventListener, o terceiro parâmetro não é passado. O padrão é excluir o evento de bolhas, porque o padrão é falso se o terceiro parâmetro não for passado. Por exemplo:
A cópia do código é a seguinte:
btndom.addeventListener ("clique", ftn, true);
btnDom.RemoveEventListener ("clique", ftn);
Execute -o e descobre que o evento não foi excluído com sucesso.
Por fim, quero dizer que o manuseio de eventos DOM2 é bem suportado no IE9, incluindo o IE9 ou acima, e o IE8 abaixo não suporta eventos DOM2.
Vamos comparar os três métodos de evento abaixo, como segue:
Comparação 1: Comparação de um método para um lado e os outros dois métodos
A maneira de escrever o método um é combinar HTML e JavaScript. Você me tem e eu tenho você. Aprofundar esse método para obter um desenvolvimento misto de HTML e JavaScript. Em um termo de software, é o acoplamento de código. O acoplamento de código não é bom e é muito ruim. Este é o nível de um programador de novato; portanto, uma vez que o método for completamente derrotado, os outros dois métodos vencerão.
Comparação 2: Método 2 e Método 3
Os dois estão escritos da mesma maneira. Às vezes é realmente difícil dizer quem é melhor ou pior. Olhando para o conteúdo acima, descobrimos que a maior diferença entre o Modo 2 e o Modo 3 é que existe apenas um evento em um elemento DOM e apenas uma vez, enquanto o Modo 3 pode permitir que um evento em um elemento DOM tenha várias funções de manuseio de eventos. No processamento de eventos DOM2, o modo 3 também pode permitir que controlemos com precisão o método de fluxo de eventos. Portanto, a função do modo 3 é mais poderosa que o modo 2; portanto, comparado ao modo 3, é um pouco melhor.
A seguir, é apresentado o foco deste artigo: os problemas de desempenho dos sistemas de eventos. Para resolver os problemas de desempenho, é preciso encontrar um foco. Aqui vou pensar nos problemas de desempenho dos sistemas de eventos de dois pontos de foco, a saber: redução do número de travessias e consumo de memória.
Primeiro de tudo, o número de travessias. Seja capturando fluxos de eventos ou fluxos de eventos borbulhantes, eles atravessarão os elementos, mas todos eles atravessarão a janela ou documento superior. Se o elemento DOM da página tiver um relacionamento pai-filho profundo, quanto mais elementos você percorrer, mais prejudicial será, como o processamento de eventos DOM2, maior a travessia. Como resolver o problema da Traversal deste fluxo de eventos? Minha resposta é não. Alguns amigos aqui podem ter perguntas, como é que não há mais? Existe um objeto de evento no sistema de eventos, que é um evento. Este objeto tem um método para evitar eventos de borbulhar ou capturar. Por que digo que não há ninguém? Essa pergunta desse amigo faz sentido, mas se quisermos usar esse método para reduzir a travessia, nosso código lidará com o relacionamento entre os elementos do pai e do filho e o relacionamento do elemento do avô. Se os elementos da página estiverem muito aninhados, essa é uma tarefa que não pode ser concluída, então minha resposta é que o problema da Traversal não pode ser alterado e só podemos nos adaptar a ele.
Parece que a redução de travessia não pode resolver o problema de desempenho do sistema de eventos, então agora podemos considerar apenas o consumo de memória. Costumo ouvir as pessoas dizerem que C# é muito útil, mas é ainda melhor para o desenvolvimento do front-end da Web. Podemos arrastar diretamente um botão para a página no C# IDE. Depois que o botão atingir a página, o código JavaScript adicionará automaticamente um evento ao botão. Obviamente, a função de evento dentro é uma função vazia. Então, acho que podemos colocar 100 botões na página dessa maneira. Se um único código não funcionar, haverá 100 botões de processamento de eventos, o que é super conveniente. Por fim, adicionamos um evento de botão específico a um dos botões para fazer a página funcionar. Esta página é eficiente? No JavaScript, cada função é um objeto e cada objeto consome memória; portanto, esse código de função de evento 99 inútil deve consumir muita memória valiosa do navegador. Obviamente, não faremos isso no ambiente de desenvolvimento real, mas na era de hoje do desenvolvimento popular e de uma página é louca e popular, existem muitos eventos em uma página da web, o que significa que cada evento tem uma função de evento, mas todas as operações que acionamos apenas um evento. Neste momento, outros eventos estão dormindo enquanto deitados, o que não desempenha nenhum papel e consome memória do computador.
Precisamos de uma solução para mudar essa situação, e de fato existe essa solução na realidade. Para descrever claramente esse plano, quero adicionar algum conhecimento de fundo primeiro. Na discussão do processamento de eventos DOM2, mencionei o conceito de objeto de destino. Deixando de lado o método de processamento de eventos DOM2, também existe o conceito de objeto de destino no processamento de eventos de captura e no processamento de eventos de bolhas. O objeto de destino é o elemento DOM da operação específica do evento. Por exemplo, o botão na operação do botão de clique é o objeto de destino. Não importa qual método de processamento de eventos, a função de evento conterá um objeto de evento. O objeto de evento possui um destino de atributo, o destino sempre aponta para o objeto de destino e o objeto de evento possui outro atributo, o CurrentTarget, que aponta para o elemento DOM que flui para o evento Capture ou Bubble. A partir da descrição acima, sabemos que, seja um evento de captura ou um evento de bolha, o fluxo de eventos fluirá para o documento. Se adicionarmos um evento de clique no documento e o botão na página não adicionar um evento de clique, clique no botão e sabemos que o evento de clique no documento será acionado. Há um detalhe aqui que, quando o evento de clique do documento é acionado, o alvo do destino do evento é o botão em vez do documento, para que possamos escrever o código como este:
A cópia do código é a seguinte:
<input type = "button" id = "btn" name = "btn" value = "button"/>
<a href = "#" id = "aa"> aa </a>
document.addeventListener ("clique", função (EVT) {
var no destino = evt.target;
Switch (Target.id) {
caso "btn":
alerta ("botão");
quebrar;
caso "aa":
alerta ("A");
quebrar;
}
},falso);
Executando, descobrimos que o efeito é o mesmo que o evento que escrevemos separadamente. Mas seus benefícios são evidentes. Uma função lida com a função de evento de toda a página e nenhuma função de evento está ociosa, o que é perfeito. Este plano também tem um nome profissional: delegação de eventos. O método do delegado da jQuery é feito de acordo com esse princípio. De fato, a eficiência da delegação de eventos não se reflete apenas na redução das funções de eventos, mas também reduz as operações de travessia de DOM. Por exemplo, no exemplo acima, adicionamos uma função no documento. O documento é o objeto de nível superior na página e a eficiência da leitura é muito alta. Quando se trata de eventos de objetos específicos, não usamos a operação DOM, mas usamos o atributo de destino do objeto de evento. Tudo isso só pode ser resumido em uma frase: é muito rápido, sem razão para ser rápido.
A delegação de eventos também pode nos trazer um ótimo subproduto. Amigos que usaram o jQuery deveriam ter usado o método ao vivo. O recurso do método LIVE é que você pode adicionar operações de evento aos elementos da página. Mesmo que esse elemento não exista na página atualmente, você pode adicionar seus eventos. Depois de entender o mecanismo de delegação de eventos, o princípio do LIVE é fácil de entender. De fato, o JQuery's Live é feito através da delegação de eventos, e o LIVE também é uma maneira eficiente de adicionar evento.
Depois de entender a delegação de eventos, descobriremos que o método de ligação do jQuery é um método ineficiente. Como ele usa o método de definição de evento original, devemos usar o Bind com cautela. De fato, os desenvolvedores de jQuery também notaram esse problema. A nova versão do jQuery tem um método on. O método on contém todas as funções dos métodos de ligação, vivos e delegados. Por isso, sugiro que amigos que tenham lido este artigo abandonem o método anterior de adicionar eventos e usar em funções para adicionar eventos.
A delegação de eventos tem outra vantagem. No exemplo da delegação de eventos no artigo acima, adicionei eventos ao documento. Aqui eu quero fazer uma comparação. No jQuery, estamos acostumados a colocar a definição de eventos de elementos DOM no método Ready, como mostrado abaixo:
A cópia do código é a seguinte:
$ (document) .ready (function () {
Xxx.bind ("clique", function () {});
});
A função pronta é executada após o carregamento do documento da página DOM. É executado primeiro que a função Onload. Isso tem muitos benefícios com antecedência. Um dos benefícios também é melhorar o desempenho. A definição de evento como o jQuery também é uma prática padrão. Acredito que alguns amigos devem vincular certos eventos fora de Ready e, finalmente, descobrir que o botão será inválido. Esse cenário inválido às vezes leva um momento e ficará bem depois de um tempo. Portanto, geralmente ignoramos o princípio desse problema e não vinculamos eventos na função pronta. Esta operação está realmente vinculando eventos antes que o DOM seja carregado e, neste período, na verdade vinculamos eventos a eles. , É muito provável que alguns elementos não tenham sido construídos na página, portanto, a ligação ao evento será inválida. Portanto, o motivo do Pronto para definir eventos é garantir que todos os elementos da página sejam carregados para definir eventos do elemento DOM após o carregamento. No entanto, os problemas podem ser evitados ao usar delegados de eventos. Por exemplo, eventos de ligação a um documento, o documento representa a página inteira, por isso é o primeiro momento para carregá -lo. Portanto, é difícil alcançar delegados de eventos no documento e também é difícil fazer com que o navegador relate "Função XXX indefinida". Para resumir esse recurso: o código do delegado de evento pode ser executado em qualquer estágio do carregamento da página, que proporcionará aos desenvolvedores maior liberdade para melhorar o desempenho da página da web ou aprimorar os efeitos da página da web.
OK, este artigo foi escrito. Boa noite.