# Teleporte

Aprenda como usar o teleporte em uma aula grátis do Vue School

O Vue nos encoraja à construir nossas interfaces de usuário (UIs) encapsulando a UI e seu respectivo comportamento em componentes. Podemos aninhar componentes dentro de outros componentes e montar uma árvore que constitui a UI da aplicação.

Entretanto, algumas vezes uma parte do template do componente pertence à esse componente de forma lógica, enquanto do ponto de vista técnico, seria preferível mover essa parte do template para outro lugar no DOM, fora da aplicação Vue.

Um cenário comum para isso é a criação de um componente que inclui um modal em tela cheia. Na maioria dos casos, você gostaria que a lógica do modal residisse dentro do componente, mas o posicionamento do modal se torna difícil de resolver por meio de CSS ou requer uma mudança na composição do componente.

Considere a seguinte estrutura HTML.

<body>
  <div style="position: relative;">
    <h3>Tooltips com Vue 3 e Teleport</h3>
    <div>
      <modal-button></modal-button>
    </div>
  </div>
</body>
1
2
3
4
5
6
7
8

Vamos olhar o modal-button de perto.

O componente terá um elemento button para acionar a abertura do modal e um elemento div com uma classe .modal que irá conter o conteúdo do modal e um botão para fechá-lo.

const app = Vue.createApp({});

app.component('modal-button', {
  template: `
    <button @click="modalOpen = true">
        Abrir modal de tela cheia!
    </button>

    <div v-if="modalOpen" class="modal">
      <div>
        Eu sou um modal! 
        <button @click="modalOpen = false">
          Fechar
        </button>
      </div>
    </div>
  `,
  data() {
    return {
      modalOpen: false
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Ao usar esse componente dentro da estrutura HTML inicial, podemos ver um problema - o modal está sendo renderizado dentro de uma div profundamente aninhada e a position: absolute do modal toma como referência a div pai posicionada relativamente.

O Teleporte fornece uma maneira limpa para nos permitir controlar sob que elemento pai no DOM nós queremos que uma parte do HTML seja rederizada, sem ter que recorrer ao estado global ou dividí-lo em dois componentes.

Vamos modificar nosso modal-button para usar o <teleport> e dizer ao Vue que "teleporte esse HTML para a tag 'body'".

app.component('modal-button', {
  template: `
    <button @click="modalOpen = true">
        Abrir modal de tela cheia! (Com Teleporte!)
    </button>

    <teleport to="body">
      <div v-if="modalOpen" class="modal">
        <div>
          Eu sou um modal teleportado!
          (Meu pai é o "body")
          <button @click="modalOpen = false">
            Fechar
          </button>
        </div>
      </div>
    </teleport>
  `,
  data() {
    return {
      modalOpen: false
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Como resultado, uma vez que clicamos no botão para abrir o modal, o Vue irá renderizar corretamente o conteúdo como um filho da tag body.

Veja o exemplo Vue 3 Teleport por vuejs-br (@vuejs-br) no CodePen.

# Usando com Componentes Vue

Se o <teleport> contém um componente Vue, o componente permanecerá um filho lógico do pai do <teleport>:

const app = Vue.createApp({
  template: `
    <h1>Instância raiz</h1>
    <parent-component />
  `
})

app.component('parent-component', {
  template: `
    <h2>Este é um componente pai</h2>
    <teleport to="#endofbody">
      <child-component name="John" />
    </teleport>
  `
})

app.component('child-component', {
  props: ['name'],
  template: `
    <div>Olá, {{ name }}</div>
  `
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Neste caso, mesmo quando o child-component é renderizado em um lugar diferente, permanecerá como filho do parent-component e receberá a propriedade name dele.

Isso também significa que injeções vindas do componente pai funcionam como o esperado, e que o componente filho será aninhado embaixo do componente pai no Vue Devtools, em vez de ser colocado no lugar para o qual o conteúdo foi movido.

# Usando Múltiplos Teleportes no Mesmo Alvo

Um cenário de caso de uso comum pode ser um componente <Modal> reutilizável, do qual pode haver várias instâncias ativas ao mesmo tempo. Para esse tipo de cenário, múltiplos componentes <teleport> podem montar seus respectivos conteúdos no mesmo elemento alvo. A ordem será simplemente anexá-los - as montagens novas serão localizadas depois das primeiras montagens realizadas no elemento alvo.

<teleport to="#modals">
  <div>A</div>
</teleport>
<teleport to="#modals">
  <div>B</div>
</teleport>

<!-- result-->
<div id="modals">
  <div>A</div>
  <div>B</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12

Você pode checar as opções do componente <teleport> nas referências da API.