Aguarde...

23 de novembro de 2020

O que há de novo no Vue 3?

O que há de novo no Vue 3?

O Vue 3 vem com muitos novos recursos interessantes e alterações em alguns dos existentes que visam tornar o desenvolvimento com o framework muito mais fácil e sustentável. Neste artigo, vamos dar uma olhada em alguns desses novos recursos e como começar a usá-los. Também daremos uma olhada em algumas das mudanças feitas nos recursos existentes.

Com o lançamento do Vue 3, os desenvolvedores precisam fazer a atualização do Vue 2, pois ele vem com um punhado de novos recursos que são muito úteis na construção de componentes fáceis de ler e de manutenção e maneiras aprimoradas de estruturar nosso aplicativo no Vue. Vamos dar uma olhada em alguns desses recursos neste artigo.

No final deste tutorial, os leitores irão;

  1. Conheça provide / injecte como usá-lo.
  2. Tenha um conhecimento básico sobre teletransporte e como usá-lo.
  3. Conheça os fragmentos e saiba como usá-los.
  4. Saiba mais sobre as mudanças feitas na API Global Vue.
  5. Conheça as alterações feitas na API de eventos.

Este artigo é direcionado àqueles que têm uma compreensão adequada do Vue 2.x. Você pode encontrar todo o código usado neste exemplo no GitHub .

provide / inject

No Vue 2.x, tornamos props mais fácil passar dados (strings, arrays, objetos, etc.) de um componente pai diretamente para seu componente filho. Mas, durante o desenvolvimento, frequentemente encontramos instâncias em que precisávamos passar dados do componente pai para um componente profundamente aninhado, o que era mais difícil de fazer props. Isso resultou no uso do Vuex Store, Event Hub e, às vezes, na transmissão de dados por meio de componentes profundamente aninhados. Vejamos um aplicativo simples;

É importante observar que o Vue 2.2.0 também veio com o provide / injectqual não foi recomendado para uso em código de aplicativo genérico.

# parentComponent.vue

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png" />
    <HelloWorld msg="Vue 3 is liveeeee!" :color="color" />
    <select name="color" id="color" v-model="color">
      <option value="" disabled selected> Select a color</option>
      <option :value="color" v-for="(color, index) in colors" :key="index">{{
        color
      }}</option></select
    >
  </div>
</template>
<script>
  import HelloWorld from "@/components/HelloWorld.vue";
  export default {
    name: "Home",
    components: {
      HelloWorld,
    },
    data() {
      return {
        color: "",
        colors: ["red", "blue", "green"],
      };
    },
  };
</script>

cópia de

# childComponent.vue

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <color-selector :color="color"></color-selector>
  </div>
</template>
<script>
  import colorSelector from "@/components/colorComponent.vue";
  export default {
    name: "HelloWorld",
    components: {
      colorSelector,
    },
    props: {
      msg: String,
      color: String,
    },
  };
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
  h3 {
    margin: 40px 0 0;
  }
  ul {
    list-style-type: none;
    padding: 0;
  }
  li {
    display: inline-block;
    margin: 0 10px;
  }
  a {
    color: #42b983;
  }
</style>

cópia de

# colorComponent.vue

<template>
  <p :class="[color]">This is an example of deeply nested props!</p>
</template>
<script>
  export default {
    props: {
      color: String,
    },
  };
</script>
<style>
  .blue {
    color: blue;
  }
  .red {
    color: red;
  }
  .green {
    color: green;
  }
</style>

cópia de

Aqui, temos uma página de destino com uma lista suspensa que contém uma lista de cores e nós estamos passando o escolhido colorpara childComponent.vuecomo um suporte. Este componente filho também possui um msgprop que aceita um texto para exibir na seção do modelo. Finalmente, este componente tem um componente filho ( colorComponent.vue) que aceita um colorprop do componente pai que é usado para determinar a classe para o texto neste componente. Este é um exemplo de como passar dados por todos os componentes.

Mas com o Vue 3, podemos fazer isso de uma forma mais limpa e curta usando o novo par Provide e injete. Como o nome indica, usamos providecomo uma função ou um objeto para disponibilizar dados de um componente pai para qualquer um de seus componentes aninhados, independentemente de quão profundamente aninhado esse componente esteja. Fazemos uso da forma de objeto ao passar valores embutidos em código para provideassim;

# parentComponent.vue

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png" />
    <HelloWorld msg="Vue 3 is liveeeee!" :color="color" />
    <select name="color" id="color" v-model="color">
      <option value="" disabled selected> Select a color</option>
      <option :value="color" v-for="(color, index) in colors" :key="index">{{
        color
      }}</option></select
    >
  </div>
</template>
<script>
  import HelloWorld from "@/components/HelloWorld.vue";
  export default {
    name: "Home",
    components: {
      HelloWorld,
    },
    data() {
      return {
        colors: ["red", "blue", "green"],
      };
    },
    provide: {
      color: 'blue'
    }
  };
</script>

cópia de

Mas, para instâncias em que você precisa passar uma propriedade de instância de componente para provide, usamos o modo de função para que isso seja possível;

# parentComponent.vue

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png" />
    <HelloWorld msg="Vue 3 is liveeeee!" />
    <select name="color" id="color" v-model="selectedColor">
      <option value="" disabled selected> Select a color</option>
      <option :value="color" v-for="(color, index) in colors" :key="index">{{
        color
      }}</option></select
    >
  </div>
</template>
<script>
  import HelloWorld from "@/components/HelloWorld.vue";
  export default {
    name: "Home",
    components: {
      HelloWorld,
    },
    data() {
      return {
        selectedColor: "blue",
        colors: ["red", "blue", "green"],
      };
    },
    provide() {
      return {
        color: this.selectedColor,
      };
    },
  };
</script>

cópia de

Como não precisamos dos coloradereços em childComponent.vuecolorComponent.vue, vamos nos livrar deles. O bom de usar provideé que o componente pai não precisa saber qual componente precisa da propriedade que está fornecendo.

Para fazer uso disso no componente que precisa nesse caso, colorComponent.vuefazemos isso;

# colorComponent.vue

<template>
  <p :class="[color]">This is an example of deeply nested props!</p>
</template>
<script>
  export default {
    inject: ["color"],
  };
</script>
<style>
  .blue {
    color: blue;
  }
  .red {
    color: red;
  }
  .green {
    color: green;
  }
</style>

cópia de

Aqui, usamos o injectque leva em uma matriz das variáveis ​​necessárias que o componente precisa. Nesse caso, precisamos apenas da colorpropriedade, portanto, apenas a passamos. Depois disso, podemos usar colorda mesma forma que usamos para usar adereços.

Podemos notar que, se tentarmos selecionar uma nova cor usando o menu suspenso, a cor não será atualizada em colorComponent.vuee isso ocorre porque, por padrão, as propriedades em providenão são reativas. Para corrigir isso, usamos o computedmétodo.

# parentComponent.vue

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png" />
    <HelloWorld msg="Vue 3 is liveeeee!" />
    <select name="color" id="color" v-model="selectedColor">
      <option value="" disabled selected> Select a color</option>
      <option :value="color" v-for="(color, index) in colors" :key="index">{{
        color
      }}</option></select
    >
  </div>
</template>
<script>
  import HelloWorld from "@/components/HelloWorld.vue";
  import { computed } from "vue";
  export default {
    name: "Home",
    components: {
      HelloWorld,
    },
    data() {
      return {
        selectedColor: "",
        todos: ["Feed a cat", "Buy tickets"],
        colors: ["red", "blue", "green"],
      };
    },
    provide() {
      return {
        color: computed(() => this.selectedColor),
      };
    },
  };
</script>

cópia de

Aqui, importamos computed e passamos nosso selectedColorpara que possa ser reativo e atualizado conforme o usuário seleciona uma cor diferente. Quando você passa uma variável para o método calculado, ele retorna um objeto que tem um value. Esta propriedade contém o valor da sua variável, portanto, para este exemplo, teríamos que atualizar colorComponent.vuepara ficar assim;

# colorComponent.vue

<template>
  <p :class="[color.value]">This is an example of deeply nested props!</p>
</template>
<script>
  export default {
    inject: ["color"],
  };
</script>
<style>
  .blue {
    color: blue;
  }
  .red {
    color: red;
  }
  .green {
    color: green;
  }
</style>

cópia de

Aqui, mudamos colorpara color.valuepara representar a mudança após torná-la colorreativa usando o computedmétodo. Nesse ponto, o classdo texto neste componente sempre mudaria sempre que fosse selectedColoralterado no componente pai.

Teleporte

Existem instâncias em que criamos componentes e os colocamos em uma parte de nosso aplicativo por causa da lógica que o aplicativo usa, mas devem ser exibidos em outra parte de nosso aplicativo. Um exemplo comum disso seria um modal ou pop-up destinado a exibir e cobrir toda a tela. Embora possamos criar uma solução alternativa para isso usando a positionpropriedade CSS em tais elementos, com o Vue 3, também podemos fazer usando o Teleport.

O teletransporte nos permite tirar um componente de sua posição original em um documento, do #appcontêiner padrão em que os aplicativos Vue são agrupados e movê-lo para qualquer elemento existente na página em que está sendo usado. Um bom exemplo seria usar o Teleport para mover um componente de cabeçalho de dentro do #appdiv para um. headerÉ importante observar que você só pode Teleportar para elementos que existem fora do Vue DOM.

O que há de novo no Vue 3?
Mensagem de erro de teleporte no console: Mensagem de erro de destino de teleporte inválida no terminal.

O componente Teleport aceita dois adereços que determinam o comportamento desse componente e eles são;

  1. to
    Este prop aceita um nome de classe, um id, um elemento ou um atributo data- * . Também podemos tornar esse valor dinâmico passando uma :toprop em oposição a toe alterando o elemento Teleport dinamicamente.
  2. :disabled
    Este prop aceita um Booleane pode ser usado para alternar o recurso de teletransporte em um elemento ou componente. Isso pode ser útil para alterar dinamicamente a posição de um elemento.

Um exemplo ideal de uso de teleporte é semelhante a este;

# index.html**

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
    <title>
        <%= htmlWebpackPlugin.options.title %>
    </title>
</head>
<!-- add container to teleport to -->
<header class="header"></header>
<body>
    <noscript>
      <strong
        >We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
        properly without JavaScript enabled. Please enable it to
        continue.</strong
      >
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
</body>
</html>

cópia de

No index.htmlarquivo padrão em seu aplicativo Vue, adicionamos um headerelemento porque queremos teletransportar nosso componente de cabeçalho para aquele ponto em nosso aplicativo. Também adicionamos uma classe a este elemento para estilização e para fácil referência em nosso componente Teleport.

# Header.vue**

<template>
  <teleport to="header">
    <h1 class="logo">Vue 3 🥳</h1>
    <nav>
      <router-link to="/">Home</router-link>
    </nav>
  </teleport>
</template>
<script>
  export default {
    name: "app-header",
  };
</script>
<style>
  .header {
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .logo {
    margin-right: 20px;
  }
</style>

cópia de

Aqui, criamos o componente de cabeçalho e adicionamos um logotipo com um link para a página inicial do nosso aplicativo. Também adicionamos o componente Teleport e atribuímos ao toprop um valor de headerporque queremos que esse componente seja renderizado dentro desse elemento. Por fim, importamos esse componente para nosso aplicativo;

# App.vue

<template>
  <router-view />
  <app-header></app-header>
</template>
<script>
  import appHeader from "@/components/Header.vue";
  export default {
    components: {
      appHeader,
    },
  };
</script>

cópia de

Neste arquivo, importamos o componente de cabeçalho e o colocamos no modelo para que fique visível em nosso aplicativo.

Agora, se inspecionarmos o elemento de nosso aplicativo, notaremos que nosso componente de cabeçalho está dentro do headerelemento;

O que há de novo no Vue 3?
Componente de cabeçalho no DevTools

Fragmentos

Com o Vue 2.x, era impossível ter vários elementos raiz no templatearquivo e, como solução alternativa, os desenvolvedores começaram a agrupar todos os elementos em um elemento pai. Embora isso não pareça um problema sério, há casos em que os desenvolvedores desejam renderizar um componente sem um contêiner envolvendo esses elementos, mas precisam se contentar com isso.

Com o Vue 3, um novo recurso chamado Fragments foi introduzido e esse recurso permite que os desenvolvedores tenham vários elementos em seu arquivo de modelo raiz. Portanto, com o Vue 2.x, é assim que se pareceria um componente de contêiner de campo de entrada;

# inputComponent.vue

<template>
  <div>
    <label :for="label">label</label>
    <input :type="type" :id="label" :name="label" />
  </div>
</template>
<script>
  export default {
    name: "inputField",
    props: {
      label: {
        type: String,
        required: true,
      },
      type: {
        type: String,
        required: true,
      },
    },
  };
</script>
<style></style>

cópia de

Aqui, temos um componente de elemento de formulário simples que aceita dois adereços, labeltype, e a seção de modelo desse componente é envolvida em um div. Isso não é necessariamente um problema, mas se você quiser que o rótulo e o campo de entrada estejam diretamente dentro do seu formelemento. Com o Vue 3, os desenvolvedores podem facilmente reescrever esse componente para ficar assim;

# inputComponent.vue

<template class="testingss">
  <label :for="label">{{ label }}</label>
  <input :type="type" :id="label" :name="label" />
</template>

cópia de

Com um único nó raiz, os atributos são sempre atribuídos ao nó raiz e também são conhecidos como atributos não prop . Eles são eventos ou atributos passados ​​para um componente que não possui propriedades correspondentes definidas em propsou emits. Exemplos de tais atributos são classid. No entanto, é necessário definir explicitamente a qual dos elementos em um componente de nó multi-raiz deve ser atribuído.

Aqui está o que isso significa usando o inputComponent.vuede cima;

  1. Ao adicionar classa este componente no componente pai, deve ser especificado a qual componente isso classseria atribuído, caso contrário, o atributo não tem efeito.
<template>
  <div class="home">
    <div>
      <input-component
        class="awesome__class"
        label="name"
        type="text"
      ></input-component>
    </div>
  </div>
</template>
<style>
  .awesome__class {
    border: 1px solid red;
  }
</style>

cópia de

Quando você faz algo assim sem definir onde os atributos devem ser atribuídos, você recebe este aviso em seu console;

O que há de novo no Vue 3?
Mensagem de erro no terminal quando os atributos não são distribuídos

bordernão tem efeito sobre o componente;

O que há de novo no Vue 3?
Componente sem distribuição de atributos
  1. Para corrigir isso, adicione um v-bind="$attrs"no elemento para o qual deseja que tais atributos sejam distribuídos;
<template>
  <label :for="label" v-bind="$attrs">{{ label }}</label>
  <input :type="type" :id="label" :name="label" />
</template>

cópia de

Aqui, estamos dizendo ao Vue que queremos que os atributos sejam distribuídos para o labelelemento, o que significa que queremos awesome__classque seja aplicado a ele. Agora, se inspecionarmos nosso elemento no navegador, veremos que a classe foi adicionada labele, portanto, uma borda agora está ao redor do rótulo.

O que há de novo no Vue 3?
Componente com distribuição de atributos

API Global

Não era incomum ver Vue.componentou Vue.useem main.jsarquivo um aplicativo Vue. Esses tipos de métodos são conhecidos como APIs globais e há um grande número deles no Vue 2.x. Um dos desafios desse método é que ele torna impossível isolar certas funcionalidades para uma instância de seu aplicativo (se você tiver mais de uma instância em seu aplicativo) sem afetar outros aplicativos porque eles são todos montados no Vue. É isso que eu quero dizer;

Vue.directive('focus', {
  inserted: el => el.focus()
})

Vue.mixin({
  /* ... */
})

const app1 = new Vue({ el: '#app-1' })
const app2 = new Vue({ el: '#app-2' })

cópia de

Para o código acima, é impossível afirmar que a Diretiva Vue esteja associada app1e o Mixin com, app2mas, em vez disso, ambas estão disponíveis nos dois aplicativos.

O Vue 3 vem com uma nova API Global em uma tentativa de corrigir esse tipo de problema com a introdução do createApp. Este método retorna uma nova instância de um aplicativo Vue. Uma instância de aplicativo expõe um subconjunto das APIs globais atuais. Com isso, todas as APIs (componente, mixin, diretiva, uso, etc.) que sofrem mutação Vuedo Vue 2.x agora serão movidas para instâncias de aplicativos individuais e agora, cada instância de seu aplicativo Vue pode ter funcionalidades que são exclusivas para eles sem afetar outros aplicativos existentes.

Agora, o código acima pode ser reescrito como;

const app1 = createApp({})
const app2 = createApp({})
app1.directive('focus', {
    inserted: el => el.focus()
})
app2.mixin({
    /* ... */
})

cópia de

No entanto, é possível criar funcionalidades que você deseja compartilhar entre todos os seus aplicativos e isso pode ser feito usando uma função de fábrica.

API De Eventos

Uma das formas mais comuns adotadas pelos desenvolvedores para passar dados entre componentes que não têm um relacionamento pai para filho, exceto pelo Vuex Store, é o uso do Event Bus . Uma das razões pelas quais esse método é comum é a facilidade de começar a usá-lo;

# eventBus.js

const eventBus = new Vue()

export default eventBus;

cópia de

Depois disso, o próximo passo seria importar esse arquivo main.jspara torná-lo disponível globalmente em nosso aplicativo ou importá-lo nos arquivos de que você precisa;

# main.js

import eventBus from 'eventBus'
Vue.prototype.$eventBus = eventBus

cópia de

Agora, você pode emitir eventos e ouvir eventos emitidos como este;

this.$eventBus.$on('say-hello', alertMe)
this.$eventBus.$emit('pass-message', 'Event Bus says Hi')

cópia de

Existe uma grande quantidade de codebase do Vue preenchida com códigos como este. No entanto, com o Vue 3, seria impossível porque $on,, $off$onceforam todos removidos, mas $emitainda está disponível porque é necessário que o componente filho emita eventos para seus componentes pai. Uma alternativa para isso seria usar provide / injectqualquer uma das bibliotecas de terceiros recomendadas .

Postado em Blog
Escreva um comentário