Articles

bitsofcode

Eu tenho escrito recentemente sobre o que são exatamente o DOM e o DOM sombra e como eles diferem. Recapitulando, o Document Object Model é uma representação baseada em objetos de um documento HTML e uma interface para manipular esse objeto. O DOM-sombra pode ser pensado como uma versão “lite” do DOM. É também uma representação baseada em objetos de elementos HTML, mas não de um documento autônomo completo. Em vez disso, o shadow DOM nos permite separar nosso DOM em bits menores, encapsulados que podem ser usados em documentos HTML.

Um outro termo similar que você pode ter encontrado é o “DOM virtual”. Embora o conceito já exista há vários anos, ele se tornou mais popular pelo seu uso no framework React. Neste artigo, vou cobrir exatamente o que é o DOM virtual, como ele difere do DOM original, e como é usado.

Por que precisamos de um DOM virtual?

Para entender porque o conceito do DOM virtual surgiu, vamos revisitar o DOM original. Como mencionei, existem duas partes do DOM – a representação baseada em objetos do documento HTML e a API para manipular esse objeto.

Por exemplo, vamos pegar esse simples documento HTML com uma lista não-ordenada e um item de lista.

<!doctype html><html lang="en"> <head></head> <body> <ul class="list"> <li class="list__item">List item</li> </ul> </body></html>

Este documento pode ser representado como a seguinte árvore DOM:

  • html
    • head lang=”pt”
    • body
      • ul class=”list”
        • li class=”list__item”
          • “List item”

Vamos dizer que queremos modificar o conteúdo do primeiro item da lista para "List item one" e também adicionar um segundo item da lista. Para fazer isso, vamos precisar usar as APIs do DOM para encontrar os elementos que queremos atualizar, criar os novos elementos, adicionar atributos e conteúdo, e finalmente atualizar os próprios elementos do DOM.

const listItemOne = document.getElementsByClassName("list__item");listItemOne.textContent = "List item one";const list = document.getElementsByClassName("list");const listItemTwo = document.createElement("li");listItemTwo.classList.add("list__item");listItemTwo.textContent = "List item two";list.appendChild(listItemTwo);

O DOM não foi feito para isso…

Quando a primeira especificação para o DOM foi lançada em 1998, nós construímos e gerenciamos páginas web de forma muito diferente. Havia muito menos confiança nas APIs do DOM para criar e atualizar o conteúdo da página tão frequentemente como fazemos hoje.

Métodos simples como document.getElementsByClassName() podem ser usados em pequena escala, mas se estamos atualizando múltiplos elementos em uma página a cada poucos segundos, pode começar a ficar muito caro para consultar e atualizar constantemente o DOM.

Até agora, devido à forma como as APIs são configuradas, geralmente é mais simples realizar operações mais caras onde atualizamos partes maiores do documento do que encontrar e atualizar os elementos específicos. Voltando ao nosso exemplo de lista, é de certa forma mais fácil substituir toda a lista não ordenada por uma nova do que modificar os elementos específicos.

const list = document.getElementsByClassName("list");list.innerHTML = `<li class="list__item">List item one</li><li class="list__item">List item two</li>`;

Neste exemplo em particular, a diferença de performance entre os métodos é provavelmente insignificante. No entanto, à medida que o tamanho da página web cresce, torna-se mais importante seleccionar e actualizar apenas o que é necessário.

… mas o DOM virtual foi!

O DOM virtual foi criado para resolver estes problemas de necessidade de actualizar frequentemente o DOM de uma forma mais performante. Ao contrário do DOM ou do DOM sombra, o DOM virtual não é uma especificação oficial, mas sim um novo método de interface com o DOM.

Um DOM virtual pode ser pensado como uma cópia do DOM original. Esta cópia pode ser frequentemente manipulada e actualizada, sem utilizar as APIs do DOM. Uma vez feitas todas as actualizações ao DOM virtual, podemos ver quais as alterações específicas que precisam de ser feitas ao DOM original e fazê-las de uma forma direccionada e optimizada.

Como se parece um DOM virtual?

O nome “DOM virtual” tende a acrescentar ao mistério do que o conceito realmente é. Na verdade, um DOM virtual é apenas um objecto Javascript normal.

Vamos revisitar a árvore do DOM que criámos anteriormente:

  • html
    • head lang=”en”
    • body
      • ul class=”list”
        • li class=”list__item”
          • “Listar item”

Esta árvore também pode ser representada como um objecto Javascript.

const vdom = { tagName: "html", children: } // end ul ] } // end body ]} // end html

Podemos pensar neste objecto como o nosso DOM virtual. Como o DOM original, ele é uma representação baseada em objetos do nosso documento HTML. Mas como é um objeto Javascript simples, podemos manipulá-lo livremente e freqüentemente sem tocar no DOM real até que precisemos.

Em vez de usar um objeto para o objeto inteiro, é mais comum trabalhar com pequenas seções do DOM virtual. Por exemplo, podemos trabalhar em um componente list, que seria o núcleo do nosso elemento de lista não-ordenado.

const list = { tagName: "ul", attributes: { "class": "list" }, children: };

Baixo do capô do DOM virtual

Agora vimos como é um DOM virtual, como funciona para resolver os problemas de performance e usabilidade do DOM?

Como mencionei, podemos usar o DOM virtual para destacar as mudanças específicas que precisam ser feitas no DOM e fazer essas atualizações específicas sozinhas. Vamos voltar ao nosso exemplo de lista sem ordem e fazer as mesmas alterações que fizemos usando a API do DOM.

A primeira coisa que faríamos seria fazer uma cópia do DOM virtual, contendo as alterações que queremos fazer. Como não precisamos usar as APIs do DOM, podemos criar um novo objeto alltogether.

const copy = { tagName: "ul", attributes: { "class": "list" }, children: };

Este copy é usado para criar o que é chamado de “diff” entre o DOM virtual original, neste caso o list, e o atualizado. Um diff poderia se parecer com isto:

const diffs = 

Este diff fornece instruções de como atualizar o DOM real. Uma vez coletados todos os difffs, podemos fazer alterações em lote no DOM, fazendo apenas as atualizações que são necessárias.

Por exemplo, poderíamos fazer um loop em cada diff e adicionar um novo filho ou atualizar um antigo, dependendo do que o diff especificar.

const domElement = document.getElementsByClassName("list");diffs.forEach((diff) => { const newElement = document.createElement(diff.newNode.tagName); /* Add attributes ... */ if (diff.oldNode) { // If there is an old version, replace it with the new version domElement.replaceChild(diff.newNode, diff.index); } else { // If no old version exists, create a new node domElement.appendChild(diff.newNode); }})

Note que esta é uma versão realmente simplificada e despojada de como um DOM virtual poderia funcionar e há muitos casos que eu não cobri aqui.

O DOM virtual e frameworks

É mais comum trabalhar com o DOM virtual através de um framework, em vez de fazer uma interface com ele diretamente como mostrei no exemplo acima.

Frameworks como React e Vue usam o conceito de DOM virtual para fazer atualizações mais performantes para o DOM. Por exemplo, o nosso componente list pode ser escrito em React da seguinte forma.

import React from 'react';import ReactDOM from 'react-dom';const list = React.createElement("ul", { className: "list" }, React.createElement("li", { className: "list__item" }, "List item"));ReactDOM.render(list, document.body);

Se quiséssemos actualizar a nossa lista, podíamos simplesmente reescrever todo o template da lista, e chamar ReactDOM.render() novamente, passando na nova lista.

const newList = React.createElement("ul", { className: "list" }, React.createElement("li", { className: "list__item" }, "List item one"), React.createElement("li", { className: "list__item" }, "List item two"););setTimeout(() => ReactDOM.render(newList, document.body), 5000);

Porque o React usa o DOM virtual, apesar de estarmos a renderizar todo o template, apenas as partes que realmente mudam são actualizadas. Se olharmos para as nossas ferramentas de desenvolvimento quando a mudança acontece, veremos os elementos específicos e as partes específicas dos elementos que mudam.

O DOM vs o DOM virtual

Para recapitular, o DOM virtual é uma ferramenta que nos permite fazer interface com os elementos do DOM de uma forma mais fácil e mais performante. É uma representação Javascript do DOM, que podemos modificar com a frequência que for necessária. As alterações feitas a este objeto são então compiladas, e as modificações no DOM real são direcionadas e feitas com menos frequência.