Quando Vue.js não pode ajudar você

Este texto é uma tradução livre do de When VueJS Can’t Help You, de Anthony Gore. Espero que goste.


Se você quer construir páginas web com JavaScript, VueJS pode fazer um trabalho maravilhoso nisso. Mas, há uma condição: ele somente trabalha em partes da sua página em que ele tem controle total. Qualquer parte que poderá sofrer interferência de script ou plugins de terceiros, não trabalha bem com o Vue.

Isto significa que as tags head e body são áreas livre do VueJS. O que se torna chato quando se quer ‘mexer’ em uma classe no body por exemplo.

Mas, enquanto Vue não pode diretamente mexer nestas tags, ele pode te ajudar, ainda, a fazer outras coisas.

Vue não se importa com as tags body e head

Por que Vue é cuidadoso acerca de onde ele trabalha?

Vue otimiza a renderização da página através do uso do Virtual-DOM. Que é uma representação em JavaScript do ‘DOM real’ que Vue mantem em memória. Atualizações no DOM geralmente são lentas, então mudanças primeiro ocorrem no V-DOM, permitindo ao Vue otimizar como ele vai atualizar o DOM (Real-DOM) através de pequenas atualizações etc.

Este sistema seria prejudicado se algum código de terceiros produzisse mudanças no DOM sem o conhecimento do Vue, causando uma incompatibilidade entre o virtual DOM e o DOM real.

Por esta razão Vue não se atém a controlar a página inteira, mas somente uma parte dela em que ele terá total controle.

O elemento mount

A primeira coisa que precisamos fazer em nosso projeto Vue é especificar um elemento para o mount do objeto de configuração através do uso da propriedade el :

new Vue({
el: '#app'
});

Isto chamará Vue na parte da página que queremos que o mesmo tenha controle. Vue terá domínio sobre este elemento e sobre seus filhos. Mas não estará habilitado a afetar qualquer elemento fora do elemento mount , seja seu irmão ou pai.

<head>
<!--Vue has no power here!-->
</head>
<body>
<!--Vue has no power here!-->
<div id="app">
<!--Vue's dominion-->
</div>
<div id="not-the-app">
<!--Vue has no power here!-->
</div>
</body>

Restrição de mount no body

Você poderia pensar que no body seria o melhor lugar para o mount , já que há boas razões para querer controlar todas as classes em body , eventos no body e afins.

O problema é que há plugins nos browsers e scripts de terceiros que poluem o body e também suas classes, event listeners até mesmo adicionando seus próprios elementos entre outras coisas.

Tudo isso seria muito caro de manter para o Vue, então a tag body não pode ser ‘montada’. Tanto que, na versão 2, se você tentar ‘montar’ uma instância nela, você recebe esse aviso no console.

"Do not mount Vue to <html> or <body> - mount to normal elements instead."

Manipulando as tags head e body

Então agora que nós estamos acordados de que Vue deverá ‘montar’ suas instâncias dentro da tag body , e que não pode afetar qualquer parte do DOM fora do nó montado, como manipular as tags head e body ?

A resposta é: não pode. Bem, não diretamente, como explicamos. Qualquer coisa fora do elemento ‘montado’ está efetivamente invisível do Vue.

Mas há mais coisa no Vue do que só renderização. Embora existam elementos além do seu alcance, ele ainda pode manipulá-los de outras formas, adicionando listeners e através do ciclo de vida de instancia no Vue.

Cenário 1: Ouvindo eventos de teclado

Vamos dizer que queira criar um modal com Vue.js e quer que o usuário seja capaz de fechá-lo com a tecla esc do teclado.

Vue.js nos traz a diretiva v-on para event listeners, mas a menos que você queria dar um foco em um input, eventos de teclado são disparado da tag body .

Uma vez que body está fora da jurisdição do Vue.js, não será possível ouvir este evento de forma tradicional. Você terá que ter seu próprio event listener usando a Web API:

var app = new Vue({
el: '#app',
data: {
modalOpen: false
}
});
document.addEventListener('keyup', function(evt) {
if (evt.keyCode === 27 && app.modalOpen) {
app.modalOpen = false;
}
});

Como Vue.js pode ajudar

Vue.js pode ajudar com os lifecycle hoks (ciclos de vida em tradução livre). Primeiramente, podemos usar o created hook para adicionar nosso listener. Isto garante que as propriedades referenciadas em data (por exemplo, modalOpen) serão observadas quando o callback for disparado.

Depois, usamos o destroyed hook para remover o listener quando não mais precisamos, para evitar desperdício de memória.

new Vue({
el: '#app',
data: {
modalOpen: false
},
methods: {
escapeKeyListener: function(evt) {
if (evt.keyCode === 27 && this.modalOpen) {
this.modalOpen = false;
}
}
},
created: function() {
document.addEventListener('keyup', this.escapeKeyListener);
},
destroyed: function() {
document.removeEventListener('keyup', this.escapeKeyListener);
},
});

Cenário 2: Gerenciando body classes

Quando um usuário abre uma modal, queremos que esteja desabilitado completamente a janela principal. Para fazer isso, podemos colocar atrás da modal, um background semi-transparente, que não pode ser clicado, e cortar qualquer possibilidade de overflow, então não pode ser possível dar um scroll.

Para prevenir o scroll, adicionamos uma classe ao body(por exemplo, modal-open ) que tem uma propriedade overflow: hidden .

body.modal-open {
overflow: hidden;
}

Obviamente que precisamos adicionar ou remover dinamicamente esta classe, pois ainda precisamos permitir a rolagem da página quando a modal for fechada. Normalmente usaríamos v-bind:class para isso, mas, novamente, body e seus atributos não estão acessíveis ao Vue.js, então teremos que recorrer à Web API novamente:

// Modal abre
document.body.classList.add('modal-open');
// Modal fecha
document.body.classList.remove('modal-closed');

Como Vue.js pode ajudar

Vue.js adiciona getters e setters reativos para cada propriedade de data, então quando um valor de data muda, ele sabe que precisa atualizar o DOM. Vue permite que nós escrevamos nossa lógica quando data muda através de watchers.

Vue executará qualquer watcher callback aonde o valor de data (neste caso, modalOpen) muda. Utilizaremos este callback para adicionar ou remover nossa classe do body .

var app = new Vue({
el: '#app',
data: {
modalOpen: false
},
watch: {
modalOpen: function(newVal) {
var className = 'modal-open';
if (newVal) {
document.body.classList.add(className);
} else {
document.body.classList.remove(className);
}
}
}
});

E é isso galera, mais um texto traduzido. Espero que possa ter ajudado você e não deixe de compartilhar e recomendar este texto. Até mais!