Maneiras de 'montar' uma instância de componente

VueJS é uma biblioteca para se criar web-components. Muitos desavisados que começam a ler sobre VueJS imaginam ele como um angularjs só que mais simples, por sorte basta alguns momentos com ele que você percebe que a pegada é outra. A principal diferença é a maneira de se criar aplicações com componentes, que possui pouca ou nenhuma semelhança com a maneira angularjs de se criar aplicações.

Na versão 1 do angular, você tinha "duas peças" o controller e a view (template), com vuejs você tem apenas componente. Em outros posts eu já falei como é a filosofia de se trabalhar com web-components, e como eles funcionam no VueJS. Nesse post darei um foco mais técnico em como esses componentes funcionam.

Vale lembrar que web-components é uma especificação e que mesmo assim ferramentas como VueJS e ReactJS não a implementam exatamente como está na especificação.


Root component

Direto ao ponto: querendo ou não tudo em VueJS gira em torno dos componentes.

import Vue from 'vue';

new Vue({  
  el: 'body',
  data: {...},
  methods: {...},
  computhed: {..},
});

Este é um exemplo corriqueiro, faz parte de todos os hello world do VueJS. Em uma ponta você tem o código javascript atuando como controller e na outra você tem a propriedade el fazendo a ligação com o elemento DOM que atuará como template.
Por isso é natural se confundir, e imaginar VueJS como uma versão simplificada do angular.

Porém, se eu te disser que você acabou de criar um componente?

Isso mesmo, esse código nada mais é do que seu root component (componente raiz). Ele existe por que o VueJS precisa de um "start", um elemento onde ele possa se "montar".

E por falar em montar.

import Vue from 'vue';

const root = new Vue({  
  data: {...},
  methods: {...},
  computhed: {..},
});

// Faz qualquer coisa elaborada antes de iniciar o componente root

// root.$mount(document.getElementById('app'));
root.$mount('#app');  

Com o exemplo anterior agora eu tenho uma outra maneira de iniciar meu componente root. VueJS se adapta muito bem a vários tipos de cenários, as possibilidades são inúmeras, tudo que você precisa é um pedaço de DOM.

Criando objetos VueJS

Como você já percebeu, um objeto VueJS é um componente, então como se não fosse suficiente, há mais uma maneira de se criar um componente root.

import Vue from 'vue';

const RootComponent = Vue.extend({  
  methods: {...},
});

const root = new RootComponent({  
  // el: 'body',
  data: {...},
});

root.$mount('body');  

Se isso é produtivo ou se você um dia vai precisar fazer algo assim, é completamente discutível, a questão é a flexibilidade proporcionada pelo VueJS é simplesmente incrível. Incrível não pela possibilidade em si, mas pela facilidade.

Manipulando seu componente root

Como eu disse, são muitas as possibilidades, o importante é você conhecer o que a instância do componente expõe para você, e inevitavelmente você vai achar um uso.

import Vue from 'vue';

const root = Vue({  
  data: {
    umaPropMuitoLoca: '1969',
  },
  methods: {...},
  computhed: {..},
});

root.$mount('#app');

// ouvindo eventos do componente
root.$on('evento:disparado:pelo:componente:root', (data) => { .. });

// algo FORA do controle do componente ocorre
root.$emit('evento:que:veio:de:fora', arg1, arg2);

// algo de FORA exige que o componente seja "desmontado"
root.$destroy(true);

//acessando dados do componente
$root.umaPropMuitoLoca = '2016';

Esse tipo de coisa pode ser feito não apenas com componentes root, é possível fazer com componentes filho também, porém isso é assunto para outro artigo.

VueJS dentro do AngularJS

Entre essas possibilidades esta a de usar VueJS dentro do angular, isso mesmo que você leu. Graças a essa flexibilidade eu estou criando um módulo novo no sistema que trabalho, totalmente com VueJS + Vuex. Não é algo que eu recomendo para quem esta começando, mas ainda é uma possibilidade para muitos.

No meu caso eu pretendo migrar todas as outras partes do projeto para VueJS, porém o mundo não é uma utopia, e preciso continuar a evolução do sistema, então nossa decisão foi de fazer esse trecho com o VueJS, e esta sendo muito bom. O fluxo de desenvolvimento não é perfeito, porém isso é insignificante se considerar a qualidade e facilidade que é trabalhar com VueJS.

O processo

Criei um projeto com vue-cli dentro da minha estrutura de pasta do projeto com angular. Após alguns ajustes eu já estava criando um bundle com o VueJS dentro da minha pasta final, onde os arquivos do angular ficam.

O projeto que criei com vue-cli esta sendo completamente ignorado pelo projeto angular, ele possui seu próprio node_modules e package.json, ele também ignora tudo relacionado ao projeto angular.

Em meu src/main.js eu tenho algo parecido com:

// OMITIDO
function MeuVueFactory() {  
  return new Vue({
    store,
    vuex: {
      actions: { resetData },
    },
    components: { MeuComponenteVuejs }
    ready() { ... },
    beforeDestroy() {
      this.resetData();
    },
  });
}

// expondo globalmente para possibilitar o uso dentro do Angular
window.MeuVueFactory;  

Como o componente precisa de um elemento DOM para ser montado, eu usei diretivas do angular para fazer este trabalho.

var DiretivaQueIniciaOVuejs = function() {  
  function link(scope, element, attrs) {
    var root = window.MeuVueFactory();
    root.$mount(element[0]);
    scope.$on("$destroy", function() {
      root.$destroy(true);
    });
  }

  return {
    restrict: 'E',
    link: link,
    template: '<span><meu-componente-vuejs></meu-componente-vuejs></span>',
  };
};

Esse tipo de integração não é algo que eu recomendo, mas cada projeto é um projeto único, então eu optei por fazer isso.


That’s all, folks!