Nota: apenas traduzi o texto abaixo e postei aqui.
DOM Manipulation nativa
Digamos que você tenha um formulário simples para adicionar itens a uma lista. Abaixo está uma lista não ordenada vazia (ul
) onde você adicionará seus "todo items".
<form id="add-todos">
<label for="new-todo">What do you want to do?</label>
<input type="text" name="new-todo" id="new-todo">
<button>Add Todo</button>
</form>
<ul id="todos"></ul>
Quando alguém envia o formulário, você deseja adicionar um item de lista (li
) com o "todo". Talvez você também queira encapsular ele em um button para que eles possam marcá-lo como concluído clicando ou tocando nele.
Também queremos evitar que o formulário tente "submit" ao server com event.preventDefault()
.
// Obtêm o field #new-todo
var todo = document.querySelector('#new-todo');
// Obtêm o container #todos
var items = document.querySelector('#todos');
document.addEventListener('submit', function (event) {
// Execute apenas quando o 'submitted form' for #add-todos
if (event.target.id !== 'add-todos') return;
// Impede que o form seja 'submitting' ao server
event.preventDefault();
// Adiciona um novo 'todo item'
var li = document.createElement('li');
li.innerHTML = '<button>' + todo.value + '</button>';
items.appendChild(li);
// Limpa o field para que o user possa adicionar um outro todo
todo.value = '';
});
Marcando como concluído
Agora, se alguém clicar em um button, queremos marcar o "todo" como concluído adicionando um strikethrough
. Também devemos estilizar nossos buttons para que não se pareçam com buttons.
Primeiro, vamos adicionar uma class
aos nossos button elements
.
document.addEventListener('submit', function (event) {
// Execute apenas quando o 'submitted form' for #add-todos
if (event.target.id !== 'add-todos') return;
// Impede que o form seja 'submitting' ao server
event.preventDefault();
// Adiciona um novo 'todo item'
var li = document.createElement('li');
li.innerHTML = '<button class="todo">' + todo.value + '</button>';
items.appendChild(li);
// Limpa o field para que o user possa adicionar um outro todo
todo.value = '';
});
Agora, podemos adicionar um click event listener
que alterna uma class .completed
em nossos buttons quando alguém clica ou toca neles.
document.addEventListener('click', function (event) {
// Somente executa em .todo buttons
if (!event.target.classList.contains('todo')) return;
// Toggle a .completed class
event.target.classList.toggle('completed');
});
E finalmente, vamos adicionar um pouco de CSS para estilizar tudo.
.todo {
background: transparent;
border: 0;
color: inherit;
font-size: 1em;
margin: 0;
padding: 0;
}
.todo.completed {
text-decoration: line-through;
}
Isso tem muito desempenho, mas também é um pé no saco.
Essa abordagem para manipulação de DOM tem muito desempenho. Estamos apenas atualizando o que mudou, minimizando a quantidade de trabalho que o navegador precisa fazer para "repaint" e "reflow" a IU.
No entanto, à medida que seu app fica maior e mais complexo, essa abordagem também se torna um pouco chata.
Por exemplo, e se você quisesse...
- Deixar as pessoas deletarem todos?
- Mostrar uma mensagem quando ainda não existem "todos items" pendentes?
- Salvar todos no localStorage e carregá-los na UI no carregamento da página?
- Suportar múltiplas listas?
De repente, você precisa ter muito mais consciência da aparência atual da IU e do que precisa mudar para chegar ao estado final desejado.
E é aqui que entram os frameworks
.
State-based UI
A razão pela qual os frameworks surgiram não foi para melhorar o desempenho da manipulação do DOM. O objetivo era tornar a UI mais fácil de gerenciar em JavaScript apps maiores.
Frameworks usam algo chamado state-based UI
.
Com a state-based UI
, você define seu estado (que é apenas uma palavra sofisticada para seus dados em um determinado momento) como um object com propriedades. Para nosso todo app, pode ser assim.
var state = {
todos: [
{
item: 'Buy a new wand',
completed: false
},
{
item: 'Get money from Gringotts',
completed: true
}
]
};
Você define um template que indica a aparência da IU com base nas diferentes propriedades do seu state.
var template = function () {
// Se não houver todos, mostre uma mensagem
if (state.todos.length < 1) {
return 'Você ainda não tem qualquer todo items. Crie um usando o form acima.';
}
// Cria uma list de todos
// https://gomakethings.com/using-array.map-to-create-markup-from-an-array-with-vanilla-js/
return '<ul>' + state.todos.map(function (todo, index) {
var completed = todo.completed ? 'completed' : '';
var html =
'<li>' +
'<button class="todo ' + completed + '" data-todo="' + index + '">' +
todo.item +
'</button>' +
'</li>';
return html;
}).join('') + '</ul>';
};
Quando alguém "submit" um novo item ou toca em um item, você atualiza seu state object
e depois instrui o framework a renderizar uma versão atualizada da UI.
Aqui está uma versão simples de vanilla JS.
document.addEventListener('submit', function (event) {
// Execute apenas quando o 'submitted form' for #add-todos
if (event.target.id !== 'add-todos') return;
// Impede que o form seja 'submitting' ao server
event.preventDefault();
// Adiciona um novo todo item
state.todos.push({
item: todo.value,
completed: false
});
// Renderiza a UI
items.innerHTML = template();
// Limpa o field para que o user possa adicionar um outro todo
todo.value = '';
});
document.addEventListener('click', function (event) {
// Somente executa em .todo buttons
if (!event.target.classList.contains('todo')) return;
// Obtêm o index do todo item
var index = event.target.getAttribute('data-todo');
// Atualiza o item no state
var completed = event.target.classList.contains('completed') ? false : true;
state.todos[index].completed = completed;
// Atualiza a UI
items.innerHTML = template();
});
DOM Diffing (Diferença de DOM)
A abordagem de state-based UI
mostrada acima é péssima para o desempenho.
Cada vez que o state é atualizado, você está reconstruindo toda a IU. Isso resulta em muitos "repaints" e "reflows" desnecessários. E é aí que entram os frameworks.
Frameworks como React e Vue fazem algo chamado DOM Diffing
.
Em vez de atualizar toda a IU, eles comparam o DOM atual com a aparência que deveria ter com base nas alterações de estado. Em seguida, eles atualizam apenas o que precisa ser atualizado – adicionando e removendo classes, injetando ou removendo elementos e assim por diante.
Em outras palavras, frameworks fazem exatamente a mesma coisa que você faz com a manipulação manual do JS DOM, usando os mesmos métodos JS subjacentes e APIs do navegador.
Então... os frameworks têm melhor desempenho do que a manipulação Vanilla JS DOM ou não?
Não, objetivamente não são. Nos bastidores, eles também estão usando a manipulação JS DOM vanilla.
Mas... se você quiser usar a state-based UI
, então uma biblioteca ou framework pode ter melhor desempenho se a alternativa for renderizar novamente toda a UI todas as vezes.
Frameworks não têm nenhum segredo de desempenho superpoderoso que o "JS normal" não tem. Eles apenas adicionam uma camada de abstração para tornar a construção de sua IU e atualizações mais fáceis (sem dúvida) à medida que o app se torna mais complexo. E essa abstração tem um custo: arquivos JS maiores que demoram mais para serem baixados e são caros para os navegadores analisarem e executarem.
Com nosso "todo list" simples, acho que a manipulação manual do DOM é mais fácil. À medida que um app como esse fica mais complexo, usar um framework pode fazer sentido.
Se você quiser usar state-based UI
, em vez de 30kb de React ou Vue, você pode usar alternativas menores como Preact, ou Svelte, ou minha própria biblioteca Reef.
Fonte
Newsletter de Go Make Things
Top comments (0)