Nuove funzionalità in Vue 3

Vue 3 è più veloce, di dimensioni inferiori e dotato di un migliore supporto TypeScript. Alcune delle nuove funzionalità che possiamo imparare a implementare in questo articolo includono: 

  • Compositio API
  • Multiple root elements (Template syntax )
  • Suspense
  • Multiple V-models
  • Migliore reattività 
  • Portals

 

Composition API

L’API di composizione è stata lanciata come plug-in alcuni mesi fa, quindi non c’è nulla di nuovo lì, ma in Vue 3 non dobbiamo più installarlo come un plug-in. Ora è integrato nel pacchetto e può essere utilizzato immediatamente senza alcuna configurazione aggiuntiva. Esistono due vantaggi principali nell’utilizzo dell’API di composizione:

  • Migliore organizzazione
  • Condividere/riutilizzare il codice

Vue 3 continuerà a supportare Options l’API, quindi se pensi di non aver bisogno di usare l’approccio a Composition API puoi sempre utilizzare i metodi tradizionali utilizzati in Vue 2. 

Se non conosci l’API di composizione, ecco come possiamo usarla per implementare un componente: 

<template>
  <div class="counter">
    <p>count: {{ count }}</p>
    <p>NewVal (count + 2): {{ countDouble }}</p>
    <button @click="inc">Increment</button>
    <button @click="dec">Decrement</button>
    <p> Message: {{ msg }} </p>
    <button @click="changeMessage()">Change Message</button>
  </div>
</template>
<script>
import { ref, computed, watch } from 'vue'
export default {
  setup() {
/* ---------------------------------------------------- */
    let count = ref(0)
    const countDouble = computed(() => count.value * 2)
    watch(count, newVal => {
      console.log('count changed', newVal)
    })
    const inc = () => {
      count.value += 1
    }
    const dec = () => {
      if (count.value !== 0) {
        count.value -= 1
      }
    }
/* ---------------------------------------------------- */
    let msg= ref('some text')
    watch(msg, newVal => {
      console.log('msg changed', newVal)
    })
    const changeMessage = () => {
      msg.value = "new Message"
    }
/* ---------------------------------------------------- */
    return {
      count,
      inc,
      dec,
      countDouble,
      msg,
      changeMessage
    }
  }
}
</script>

E qui c’è il codice equivalente in Options API:

<template>
  <div class="counter">
    <p>count: {{ count }}</p>
    <p>NewVal (count + 2): {{ countDouble }}</p>
    <button @click="inc">Increment</button>
    <button @click="dec">Decrement</button>
    <p> Message: {{ msg }} </p>
    <button @click="changeMessage()">Change Message</button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      count: 0,
      msg: 'some message'
    }
  },
  computed: {
    countDouble() {
      return this.count*2
    }
  },
  watch: {
    count(newVal) {
      console.log('count changed', newVal)
    },
    msg(newVal) {
      console.log('msg changed', newVal)
    }
  },
  methods: {
     inc() {
      this.count += 1
    },
    dec() {
      if (this.count !== 0) {
        this.count -= 1
      }
    },
    changeMessage() {
      msg = "new Message"
    }
  }
 
}
</script>

Possiamo vedere che l’utilizzo dell’API di composizione ci consente una migliore organizzazione, mantenendo insieme il codice (stato, metodi, proprietà calcolate, osservatori ecc.) di particolari funzionalità, cosa che non era possibile nell’API delle opzioni. 

Nell’esempio sopra, il codice per il counter e il codice per la modifica di un message sono chiaramente separati nell’API di composizione. 

Man mano che il componente cresce di dimensioni, l’organizzazione del codice diventa un fattore importante. Qualsiasi nuovo sviluppatore può facilmente comprendere il codice senza perdere troppo tempo ad analizzare tutte le righe di codice. Prima, potevamo usare Mixins per condividere il codice. 

Tuttavia, era difficile tenere traccia degli stati e dei metodi in diversi componenti e Mixins aveva il potenziale per sovrascrivere lo stato o i metodi esistenti nei nostri componenti, se non fossimo stati attenti. 

L’utilizzo dell’API Composition rende la condivisione del codice molto più semplice. Possiamo scomporre il codice di una particolare funzione e utilizzarlo in più posti, come mostrato di seguito:

//message.js
import { ref, watch } from 'vue'
export function message() {
  let msg = ref(123)
  watch(msg, newVal => {
    console.log('msg changed', newVal)
  })
  const changeMessage = () => {
    msg.value = 'new Message'
  }
  return { msg, changeMessage }
}

Utilizzo del codice condiviso nel nostro componente:

<template>
  <div class="counter">
    <p>count: {{ count }}</p>
    <p>NewVal (count + 2): {{ countDouble }}</p>
    <button @click="inc">Increment</button>
    <button @click="dec">Decrement</button>
    <p>Message: {{ msg }}</p>
    <button @click="changeMessage()">change message</button>
  </div>
</template>
<script>
import { ref, computed, watch } from 'vue'
import { message } from './common/message'
export default {
  setup() {
    let count = ref(0)
    const countDouble = computed(() => count.value * 2)
    watch(count, newVal => {
      console.log('count changed', newVal)
    })
    const inc = () => {
      count.value += 1
    }
    const dec = () => {
      if (count.value !== 0) {
        count.value -= 1
      }
    }
    let { msg, changeMessage } = message()
    return {
      count,
      msg,
      changeMessage,
      inc,
      dec,
      countDouble
    }
  }
}
</script>

Fare riferimento alla guide ufficiale dell’API di composizione per maggiori dettagli.

 

Multiple root elements (template syntax )

In Vue 2, il tag template può prendere solo un elemento radice. Anche se avessimo solo due tag  <p>, dovevamo racchiuderli in un tag <div> per farlo funzionare. Per questo motivo, abbiamo dovuto modificare anche il codice CSS nel componente padre in modo che appaia come previsto. 

In Vue 3, questa restrizione viene revocata. Non è più necessario un root element. 

Possiamo utilizzare qualsiasi numero di tag direttamente all’interno della sezione <template></template>:

<template>
  <p> Count: {{ count }} </p>
  <button @click="increment"> Increment </button>
  <button @click="decrement"> Decrement</button>
</template>


Codice equivalente in Vue 2:

<template>
  <div class="counter">
    <p> Count: {{ count }} </p>
    <button @click="increment"> Increment </button>
    <button @click="decrement"> Decrement</button>
  </div>
</template>

 

Suspense

 La sospensione è una nuova funzionalità che esegue il rendering di un componente predefinito/di fallback finché il componente principale non recupera i dati. 

A volte utilizziamo operazioni asincrone per recuperare i dati dal server. Invece di consegnare il modello con v-if e poi reimpostarlo quando restituiamo i dati, Suspense lo fa per noi. 

La suspense può essere utilizzata per entrambe le parti del modello o per l’intero modello:

<template>
  <Suspense>
    <template #default>
      <div v-for="item in articleList" :key="item.id">
        <article>
          <h2>{{ item.title }}</h2>
          <p>{{ item.body }}</p>
        </article>
      </div>
    </template>
    <template #fallback>
      Articles loading...
    </template>
  </Suspense>
</template>
<script>
import axios from 'axios'
export default {
  async setup() {
    let articleList = await axios
      .get('https://jsonplaceholder.typicode.com/posts')
      .then(response => {
        console.log(response)
        return response.data
      })
    return {
      articleList
    }
  }
}
</script>

 

Multiple v-models

Sappiamo tutti che il modello v viene utilizzato per l’associazione a due vie. Lo usiamo principalmente con gli elementi del modulo. A volte, lo usiamo anche con componenti personalizzati. 

Vue-2 ha permesso l’uso di un solo modello v su un componente. In Vue-3, possiamo associare un numero qualsiasi di v-model ai nostri componenti personalizzati:

<template>
      <survey-form v-model:name="name" v-model:age="age"> </survey-form>
    </template>



    //SurveyForm.vue
    <template>
      <div>
        <label>Name: </label>
        <input :value="name" @input="updateName($event.target.value)" />
        <label>Age: </label>
        <input :value="age" @input="updateAge($event.target.value)" />
      </div>
    </template>
    <script>
    export default {
      props: {
        name: String,
        age: Number
      },
      setup(props, { emit }) {
        const updateName = value => {
          emit('update:name', value)
        }
        const updateAge = value => {
          emit('update:age', +value)
        }
        return { updateName, updateAge }
      }
    }
    </script>

 

Migliore reattività 

Vue 2 aveva già una grande reattività. Tuttavia, ci sono stati alcuni casi in cui Vue 2 non è stato all’altezza. 

Ripercorriamo Vue 2 e vediamo quali erano queste limitazioni. 

Per dimostrare la reattività, utilizzeremo i watchers per ascoltare una delle variabili di stato e quindi modificarla per vedere se i watchers vengono attivati:

<template>
  <div class="hello" @click="test">test {{list }} {{ myObj }}</div>
</template>
<script>
export default {
  name: "HelloWorld",
  data() {
    return {
      list: [1, 2],
      myObj: { name: "Preetish" }
    };
  },
  watch: {
    list: {
      handler: () => {
        console.log("watcher triggered");
      },
      deep: true
    }
  },
  methods: {
    test() {
      this.list[2] = 4;
      this.myObj.last = "HS";
      delete this.myObj.name;
    }
  }
};
</script>

Nessuna delle tre modifiche precedenti – come l’aggiunta di un nuovo elemento a un array in base all’indice, l’aggiunta di un nuovo elemento a un oggetto o l’eliminazione di un elemento dall’oggetto – è reattiva in Vue-2. Quindi i watchers  non verranno attivati o il DOM verrà aggiornato. Abbiamo dovuto usare i metodi vue.set() o vue.delete().

In Vue-3, questi funzionano direttamente senza alcuna funzione di supporto:

export default {
  setup() {
    let list = ref([1, 2])
    let a = ref(0)
    let myObj = ref({ name: 'Preetish' })
    function myFun() {
      list.value[3] = 3
      myObj.value.last = 'HS'
      delete myObj.value.name
    }
    return { myFun, list, myObj }
  }
}

Possiamo notare come il watcher è stato attivato tutte le quattro volte nel setup Vue 3 .

 

Global mounting

Quando apri main.js nel progetto about, noterai qualcosa di diverso. Non utilizziamo più l’istanza Global Vue per installare plug-in e altre librerie. 

Invece, puoi vedere il metodo createApp:

import { createApp } from 'vue'
import App from './App.vue'
const myApp = createApp(App)
myApp.use(/* plugin name */)
myApp.use(/* plugin name */)
myApp.use(/* plugin name */)
myApp.mount('#app')

Il vantaggio di questa funzione è che protegge l’applicazione Vue da librerie/plugin di terze parti che utilizziamo che potrebbero sovrascrivere o apportare modifiche all’istanza globale, principalmente utilizzando Mixin. 

Ora con il metodo createApp, installiamo quei plugin su una particolare istanza e non sull’oggetto globale.

 

Portali

Portal è una funzionalità in cui possiamo eseguire il rendering di una parte del codice presente in un componente in un componente diverso in un albero DOM diverso. C’era un plug-in di terze parti chiamato portal-vue che ha raggiunto questo obiettivo in Vue 2. 

In Vue 3, il portale sarà integrato ed è molto facile da usare. 

Vue 3 avrà un tag speciale chiamato  <Teleport> e qualsiasi codice racchiuso in questo tag sarà pronto per essere teletrasportato ovunque. Il tag Teleport porta a un argomento to . 

Vediamolo in azione:

<Teleport to="#modal-layer">
  <div class="modal">
      hello
  </div>
</Teleport>

Qualsiasi codice dentro  <Portal></Portal> verrà visualizzato nella posizione di destinazione menzionata.

<div id="modal-target"></div>

Al momento della scrittura di questo articolo, <Teleport> non funziona nella versione Alpha menzionata sopra. 

 

Conclusione

Se stai pianificando di iniziare il tuo nuovo progetto, puoi comunque andare avanti e utilizzare Vue 2 con un plug-in API di composizione e successivamente migrare a Vue 3 poiché non ci saranno modifiche di rilievo oltre alla rimozione dei filtri. 

Vue 3 sarà ricco di molte nuove e sorprendenti funzionalità. La composizione integrata avrà un impatto significativo sul flusso di sviluppo nelle app future fornendo un modo semplice per organizzare e condividere il codice con un ottimo supporto TypeScript.

Le prestazioni verranno ottimizzate e le dimensioni del pacchetto saranno ulteriormente ridotte nel nuovo aggiornamento in arrivo.

Altre funzionalità come Suspense, multiple v-models, ecc. renderanno lo sviluppo più semplice di prima.

Prova le tue app Vue esattamente come fa un utente 

Il debug delle applicazioni Vue.js può essere difficile, soprattutto quando ci sono dozzine, se non centinaia di mutazioni durante una sessione utente. Se sei interessato a monitorare e tracciare le mutazioni Vue per tutti i tuoi utenti in produzione, prova LogRocket.

https://logrocket.com/signup/

LogRocket è come un DVR per app Web, che registra letteralmente tutto ciò che accade nelle tue app Vue, incluse richieste di rete, errori JavaScript, problemi di prestazioni e molto altro. Invece di indovinare il motivo per cui si verificano i problemi, puoi aggregare e segnalare lo stato in cui si trovava la tua applicazione quando si è verificato un problema. 

Il plug-in LogRocket Vuex registra le mutazioni Vuex nella console LogRocket, fornendo un contesto su cosa ha portato a un errore e in quale stato si trovava l’applicazione quando si è verificato un problema.