A melhor maneira de trabalhar com o DOM

Enquanto escrevia no Solid , tive a oportunidade de estimar o número de tentativas para otimizar o desempenho em gráficos (benchmarks).

O DOM é um desenvolvimento de gargalo front-end. Soluções diferentes podem levar a resultados semelhantes. Novas bibliotecas aparecem todas as semanas, incorporando o melhor das anteriores, para fazer a combinação perfeita. Depois de algum tempo, meus olhos começam a fugir de uma variedade de soluções.

Não me entenda mal. Adoro ver novas idéias surgirem, mas todas elas apresentam falhas e, às vezes, acarretam uma perda de conveniência ou produtividade. Entendo as consequências dessa decisão, mas quero compartilhar minhas observações.

Qual é a maneira mais rápida de trabalhar com o DOM:

  • Dom virtual
  • Literais de modelo com a tag
  • Observáveis ​​refinados

Comparação


O JS Frameworks Benchmark é o melhor projeto de código aberto para comparar o desempenho das estruturas de UI do JavaScript. É melhor executar testes localmente, em vez de usar resultados oficiais. Os resultados podem variar de acordo com a máquina. Como estou testando em uma máquina fraca, o desempenho diminuirá notavelmente.

Adotarei as melhores abordagens na renderização de uma árvore DOM para ilustrar a redundância de certas soluções. Farei isso usando a capacidade do Solid de suportar diferentes opções de renderização para impor custos à medida que as variáveis ​​mudam e compará-las com resultados semelhantes de outras estruturas. Vamos dar uma olhada neles.

Variedades de Sólido:

  • solid - versão da estrutura com o configurador de proxy ES2015 sobre as funções de controle de alterações integradas nos modelos de nós DOM clonados. Isso é obtido pré-compilando modelos JSX (código)
  • sinais sólidos - esta versão é igual à anterior, mas sinais brutos são usados ​​em vez de proxies. Isso complica o uso da biblioteca, mas no final obtemos um pacote menor e melhor desempenho (código)
  • aceso - esta versão não usa a pré-compilação JSX em tempo de execução (código)
  • solid-h - Esta versão usa o HyperScript para criar `document.createElement` em tempo real. O restante usa a mesma implementação que Solid (Code)

Outras bibliotecas:

  • domc — -, DOM DSL (domain specific language) HTML, index.html (Code)
  • surplus — JSX `document.createElement`. Solid (Code)
  • ivi — Inferno, DOM. HyperScript Helpers-esque (Code)
  • lit-html — , c . Tagged Template Literals DOM- (Code)
  • inferno é o mais rápido dos clones React e uma das bibliotecas virtuais mais rápidas do DOM. Usa diretivas JSX especiais para obter melhor desempenho (código)

Algumas de suas estruturas favoritas podem não estar aqui, mas esta lista exibe versões otimizadas de todas as técnicas que você verá nas estruturas mais populares. Você pode vê-lo como um indicador para avaliar os recursos máximos da sua biblioteca. Se você estiver interessado em comparar o desempenho de estruturas populares, recomendo este gráfico.

Para comparação, gostaria de adicionar o Web Assembly. Infelizmente, no momento da redação deste artigo, os registros WASM eram uma implementação de baunilha sem abstrações de alto nível. (Mais tarde, eles adicionaram wasm-bindgen à estrutura - aprox. Tradutor)

resultados


HyperScript (inferno, ivi, sólido-h)


O HyperScript exibe a marcação como uma composição de funções (como h ou React.createElement). Por exemplo:

h('div', {id: 'my-element'}, [
  h('span', 'Hello'),
  h('span', 'John')
])

As estruturas virtuais do DOM possuem essa propriedade. Mesmo se eles usarem JSX ou outros mecanismos de modelo DSL - ainda serão convertidos para métodos de renderização elemento a elemento. Isso é usado para construir uma árvore DOM virtual para cada ciclo de renderização. Mas, como mostrado aqui, as funções renderizadas podem ser usadas para criar um gráfico de dependência reativa, como no caso de Solid.



Como você pode ver, as bibliotecas com um DOM virtual são muito mais rápidas. Sólido perde desempenho devido à criação excessiva de um gráfico reativo. Observe a diferença nos pontos de referência # 1, # 2, # 7, # 8, # 9.



A memória é menos convincente. O Inferno e esta versão do Solid mostram aproximadamente o mesmo resultado. Enquanto o ivi usa mais memória.
, Solid, , VDOM. Solid , DOM, DOM. Solid JSX DOM . , . Solid fine grained evaluation . - .

As estruturas de rastreamento de atualização reativa mostram melhores resultados ao atualizar linhas. Este gráfico explicaria a popularidade do VDOM nos últimos anos. Basta dizer que, se você usar o HyperScript com esta atualização, é melhor alternar para o Virtual DOM.

Modelos de string (domc, lit-html, solid-lit)


Cada biblioteca aqui tem algo em comum. Eles são renderizados com base em modelos de elemento de clonagem, executados em tempo de execução e não usam VDOM. Mas eles ainda têm diferenças. O DomC e o lit-html usam diferenças de cima para baixo semelhantes ao Virtual DOM, enquanto o Solid usa um gráfico reativo. O Lit-html divide os modelos em partes. DomC e Solid compilam o modelo em caminhos separados em tempo de execução e os atualizam.



Esta categoria tem a maior variedade de desempenho. O DomC é o mais rápido e o lit-html é o mais lento. Solid Lit está no meio. O DomC demonstra que a simplicidade do código leva ao melhor desempenho.

O DomC afunda apenas na seção 4 porque calcula a diferença de nós, que se torna mais complicada à medida que a profundidade aumenta. É bem rápido, mas você precisa validar os resultados no big data.

Solid Lit é mais produtivo que Solid HyperScript. A compilação instantânea em tempo de execução remove as desvantagens de criar um gráfico reativo, permitindo que a estrutura acompanhe o ivi, a biblioteca VDOM mais rápida (consulte a tabela completa no final do artigo).



DomC mostrou bons resultados no consumo de memória. Isso aconteceu devido à clonagem dos elementos do modelo. Vale ressaltar que a geração de código em tempo de execução pode ter uma sobrecarga de desempenho mínima em comparação com a compilação no estágio de compilação. Talvez esta seja uma comparação injusta para o lit-html porque a estrutura não usa essa técnica. Mas é justo dizer que bibliotecas lit-html ou similares, como hyperHTML ou lighterHTML, não são a melhor maneira de implementar literais de modelo com tags. E você pode obter bons resultados, mesmo em tempo de execução, sem o VDOM.

JSX pré-compilado (sólido, sinais sólidos, excedente)


Essas bibliotecas usam JSX, que é compilado em um DOM ou gráfico reativo no estágio de construção. Os modelos podem ser qualquer coisa, mas o JSX fornece uma árvore de sintaxe limpa que aprimora a experiência do desenvolvedor.



Este grupo tem resultados semelhantes, mas a diferença é muito importante. Todos os três utilizam a mesma biblioteca para gerenciar S.js estado . Usando o exemplo de Sinais Sólidos, você pode ver que as funções de rastreamento com clonagem de elementos do modelo oferecem melhor desempenho. A implementação padrão do Solid está sobrecarregada usando os Proxies ES2015, o que piora o resultado em todos os gráficos. O excedente usa `document.createElement`, que diminui o desempenho em testes nos quais as linhas 1, 2, 7 e 8 são criadas.



O consumo de memória tem resultados semelhantes. Nesse caso, os proxies adicionam mais complexidade do que os elementos do modelo de clonagem.

A conclusão aqui é que os proxies degradam o desempenho e mais bibliotecas devem clonar modelos. Por outro lado, você pode considerar uma pequena perda de desempenho devido a proxies como investimento. O exemplo Solid possui a menor quantidade de código dentre outros exemplos - apenas 66 linhas, 13% menos espaço em branco que o Svelte - uma biblioteca que se orgulha de seu minimalismo.

Melhor da classe (domc, ivi, solid-signs, vanillajs)


Agora vamos pegar os vencedores de cada categoria e compará-los com o exemplo brutal, eficaz e escrito à mão em JavaScript vanilla. Cada implementação representa uma das populares soluções de rastreamento de estado. Você pode até fazer uma analogia entre essas bibliotecas e os Três Grandes: Sólido → Vue, DomC → Angular, ivi → Reagir. Você obterá esse resultado se remover tudo supérfluo, exceto a renderização, e se livrar do código de 60-200kb.



O DomC e o Solid estão próximos em termos de desempenho, o ivi está significativamente atrasado, mas o DomC, no geral, é mais rápido. Sua complexidade em comparação com o vanillaJS é notavelmente menor, mas é menos eficaz com atualizações parciais. Somente este critério não é indicativo. Qualquer pessoa que pense que o VDOM é lento ou tem complicações desnecessárias deve verificar isso por conta própria.

A maioria das bibliotecas nunca terá esse tipo de desempenho.



O DomC também está liderando o gráfico com memória. Sólido refinado supera o VDOM ivi em termos de consumo de memória.

Curiosamente, essas bibliotecas não são muito piores que o vanillaJS, independentemente do método. Eles são todos muito rápidos.

Tamanho do pacote


Por fim, gostaria de abordar o tamanho do pacote. Muitos testes reais se concentram apenas nessas métricas. Sim, o tamanho do pacote é importante e tem uma correlação direta com o desempenho, mas qual é a diferença? Suspeito que a complexidade do código seja mais importante que o tamanho.



Conclusão


Como sempre, os resultados nesses gráficos nunca são completamente convincentes. O processo em si e as conclusões que tiramos são importantes. Nesse caso, vemos que o próprio DOM é um grande gargalo no que diz respeito ao desempenho. Tanto é assim que não existe uma técnica inequívoca para contorná-lo.


Christoper Lambert como o Highlander

Não, não é assim tão simples. Nem o DOM nem o VDOM são lentos. Mas acredito que eles valem um ao outro. Admito que a retórica do VDOM Performance React me levou a esses pensamentos. A ignorância de opiniões sobre esse tópico é enfurecedora.

A alegação de que o VDOM é lento deve-se à falta de conhecimento. Renderizar VDOM e calcular a diferença de estado é uma complicação comparada a não fazê-lo. Mas sua ausência é escalável? E como fazer alterações nos dados?

Vejo que há uma exceção em todas as regras. Em geral, a pré-compilação combinada com granulação fina em estruturas reativas é a solução mais rápida. Mas o DomC mostra alto desempenho sem ele. Métodos JS nativos, como clonagem de elementos de modelo com literais de modelo marcado, podem ser a melhor solução para implementar lit-html de grandes empresas (Google). Mas essa é uma das estruturas mais lentas desta tabela e nem mesmo a melhor implementação dessas tecnologias. Svelte é considerada a biblioteca mais rápida da comunidade, mas não conseguiu nem competir de perto com as soluções apresentadas.

Se a programação reativa vencer, isso não significa que todas as bibliotecas reativas são rápidas ou que métricas significam tudo. Apesar da comparação profunda neste artigo, acho que, na realidade, existem bibliotecas rápidas e lentas. Mesmo se encontrarmos super tecnologia, ainda enfrentaremos suas limitações.

Resultados de teste de todas as bibliotecas em uma tabela:


All Articles