back to top

I mixin in Vue.js

I mixin sono uno strumento per poter isolare e condividere frammenti di codice fra più componenti. In questo modo non dovremo duplicare inutilmente funzioni e dati analoghi in diverse parti di un’applicazione.

Prima di continuare, è bene precisare che con l’introduzione della Composition API in Vue 3, l’uso dei Mixin è sconsigliato.

Tipi di Mixin

Esistono due tipi di mixin:

  • Mixin locali: il loro raggio di azione è limitato al componente che li importa e li registra. Questo è il tipo di mixin su cui concentreremo la nostra attenzione nel corso della lezione corrente.
  • Mixin globali: vengono registrati tramite il metodo Vue.mixin() e hanno effetto su tutti i componenti di un progetto. Per questa ragione è sconsigliato usare dei Mixin a livello globale.

Perché sono stati introdotti i Mixin

I Mixin sono stati introdotti con l’intento di fornire uno strumento agli sviluppatori per riutilizzare porzioni di codice in più componenti evitando di dover duplicare inutilmente funzioni e dati in più parti di un’applicazione.

Supponiamo per esempio di avere due differenti componenti che per semplicità definiamo nei file Component1.vue e Component2.vue. Entrambi i componenti condividono un metodo toggleVisibility ed una proprietà booleana show per mostrare o nascondere degli elementi del template.

// Component1.vue
<template>
  <div>
    <div v-show="show">
      <label for="nickname">Nickname</label>
      <input 
        type="text" 
        id="nickname" 
        placeholder="nickname" 
        v-model="name"
      >
    </div>
    <button @click="toggleVisibility">
      Nascondi campo 'nickname'
    </button>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        name:'',
        show: true
      }
    },
    methods: {
      toggleVisibility() {
        this.show = !this.show;
      }
    }
  }
</script>
// Component2.vue
<template>
  <div>
    <p v-if="show">
      Cras justo odio, dapibus ac facilisis in, egestas eget quam. 
      Aenean lacinia bibendum nulla 
      sed consectetur. Maecenas sed diam 
      eget risus varius blandit sit amet non magna. 
      Maecenas faucibus mollis interdum.
    </p>
    <button @click="toggleVisibility">
      Nascondi Paragrafo
    </button>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        show: true
      }
    },
    methods: {
      toggleVisibility() {
        this.show = !this.show;
      }
    }
  }
</script>

Dal momento che entrambi i componenti contengono lo stesso metodo e la proprietà show, la soluzione appena illustrata non è sicuramente la migliore. Infatti se volessimo utilizzare la stessa funzionalità in un altro componente, dovremmo riscrivere e ripetere inutilmente le stesse porzioni di codice.

Se poi fosse necessario apportare delle modifiche, dovremmo andare a correggere ciascuno dei componenti.

È facile quindi capire che, definendo i componenti come nell’esempio appena esposto e ripetendo inutilmente porzioni di codice uguali, introduciamo degli evidenti limiti sia in termini di organizzazione e manutenibilità del codice che per efficienza e gestione delle risorse.

Creare, registrare e usare un mixin

I mixin consentono di porre rimedio a questo tipo di situazioni incapsulando funzioni e dati all’interno di un oggetto che viene poi importato ed incluso dai diversi componenti.

Possiamo allora riscrivere l’esempio mostrato sopra estraendo dai componenti le funzionalità comuni ed isolandole all’interno di un oggetto che esportiamo dal file toggleVisibilityMixin.js (In questo caso non si tratta di un single file component con estensione .vue, ma di un normale file javascript).

// file: toggleVisibilityMixin.js
export default {
  data() {
    return {
      show: true,
    };
  },
  methods: {
    toggleVisibility() {
      this.show = !this.show;
    }
  }
}

Nel file toggleVisibilityMixin.js creiamo un oggetto che contiene alcune delle proprietà che useremmo per definire un componente tramite un oggetto di opzioni. Nel caso specifico abbiamo estratto i dati e i metodi comuni ai due componenti.

All’interno dei componenti importiamo poi i due mixin e li registriamo localmente tramite l’opzione mixins che accetta come valore un array in cui ogni elemento è un riferimento ad un mixin.

// Component1.vue
<template>
  <div>
    <div v-show="show">
      <label for="nickname">Nickname</label>
      <input type="text" id="nickname" placeholder="nickname" v-model="name">
    </div>
    <button @click="toggleVisibility">Nascondi campo 'nickname'</button>
  </div>
</template>

<script>
import toggleVisibility from './toggleVisibilityMixin';

export default {
  // registriamo il mixin
  mixins: [toggleVisibility],
  // ogni componente può continuare ad avere
  // dei dati, metodi e altre proprietà che verranno
  // eventualmente uniti con quelli presenti nel mixin
  data() {
    return {
      name:''
    }
  }
}
</script>
// Component2.vue
<template>
  <div>
    <p v-if="show">
      Cras justo odio, dapibus ac facilisis in, egestas eget quam. 
      Aenean lacinia bibendum nulla sed consectetur. 
      Maecenas sed diam eget risus varius blandit sit amet non magna. 
      Maecenas faucibus mollis interdum.
    </p>
    <button @click="toggleVisibility">Nascondi Paragrafo</button>
  </div>
</template>

<script>
import toggleVisibility from './toggleVisibilityMixin';

export default {
  // registriamo il mixin
  mixins: [toggleVisibility]
}
</script>

Le proprietà importate dal mixin vengono quindi unite a quelle già presenti nel componente che alla fine conterrà anche tutti i metodi e le proprietà del mixin.

Facendo riferimento all’esempio, l’oggetto di dati di Component1 presenterà alla fine sia la proprietà show (importata dal mixin) che name.

È bene evidenziare che i componenti non condividono la stessa istanza dell’oggetto esportato dal Mixin. Per questo motivo nei due componenti da noi definiti, il valore della proprietà show sarà indipendente.

All’interno di un mixin possiamo avere tutte le proprietà dell’oggetto di opzioni usato per definire un componente compresi i lifecycle hooks, le computed properties, watchers e così via.

Una volta registrati in un componente, le diverse proprietà vengono combinate con quelle del componente stesso seguendo delle strategie predefinite di cui parleremo a breve.

Limiti dei mixin

Sebbene rappresentino un modo per riutilizzare il codice in più componenti, i mixin presentano diversi problemi e limitazioni.

Il primo evidente inconveniente è legato al nome delle proprietà usate nei mixin e al rischio di collisione con quelle già presenti nei componenti. Per le strategie di inclusione, le proprietà dei componenti sovrascrivono le omonime proprietà dei mixin.

Inoltre, i componenti possono accedere alle proprietà dei mixin e questi, a loro volta, possono far riferimento ad eventuali proprietà dei componenti con l’assunzione che queste siano presenti. Volendo procedere ad effettuare delle modifiche o a ristrutturare il codice, diventa allora complicato gestire tutte le dipendenze.

Se poi registriamo diversi mixin all’interno di un componente, non è sempre immediato capire da quale mixin proviene una certa funzionalità in quanto i mixin agiscono come una sorta di scatola chiusa che includiamo in un componente senza specificare in modo chiaro ed esplicito cosa viene poi importato. Può quindi risultare difficile risalire alle cause di eventuali comportamenti non aspettati.

Per le ragioni esposte sopra, i mixin rischiano a volte di diventare degli strumenti poco prevedibili e non facilmente riutilizzabili. Per questo motivo a partire dalla versione 3 di Vue è sconsigliato usarli. Al loro posto è suggerito impiegare la Composition API che rappresenta un modo alternativo ed opzionale per definire i componenti.

Strategie di inclusione e risoluzione dei conflitti

Per risolvere il problema dei conflitti dei nomi, il processo di inclusione delle proprietà e metodi definiti nei mixin segue delle regole prestabilite.

I mixin vengono applicati prima dei componenti in modo tale che, in caso di conflitti, l’ultima parola spetta al componente che provvede a sovrascrivere proprietà con lo stesso nome. Oggetti contenenti più proprietà vengono unificati e, in caso di proprietà con lo stesso nome, hanno la meglio quelle presenti nel componente.

L’unica eccezione vale per i lifecycle hooks. Se lo stesso metodo del ciclo di vita è definito sia in un mixin che in un componente, le due implementazioni vengono unite in un array cosicché entrambi i metodi vengano eseguiti. I lifecycle hooks presenti nei mixin vengono invocati prima degli analoghi metodi presenti in un componente.

export default {
  created() {
    console.log('mixin hook invocato prima del componente');
  }
}
import myMixin from './myMixin';

export default {
  mixins: [myMixin],
  created() {
    console.log('metodo del componente eseguito dopo quello del mixin');
  }
}

Nella console degli sviluppatori del browser verranno stampati in ordine i due messaggi.

// mixin hook invocato prima del componente
// metodo del componente eseguito dopo quello del mixin

Riepilogo

In questa lezione abbiamo parlato dei mixin che rappresentano un modo per incapsulare e riutilizzare delle funzioni fra più componenti. Abbiamo visto che sono stati introdotti per meglio organizzare il codice ed evitare di duplicare funzioni e dati analoghi. Costituiscono inoltre un primo tentativo per organizzare un componente in base alle sue funzionalità invece di strutturarlo per opzioni. I mixin hanno tuttavia numerose limitazioni che li rendono imprevedibili e difficili da usare. Sono infatti soggetti a problemi di conflitti dei nomi delle proprietà e dei metodi. Per questo motivo sono state definite delle strategie per completare il processo di inclusione delle funzionalità di un mixin all’interno di un componente.

A causa della poca praticità di uso, il basso livello di controllo e le difficoltà nel determinare chiaramente quale mixin contiene delle specifiche funzioni, nella versione 3 di Vue non è più consigliato il loro utilizzo.

Anche per risolvere le problematiche per cui erano stati introdotti i mixin, in Vue 3 è stata aggiunto un nuovo modo per definire un componente, ovvero la Composition API che sarà l’argomento della prossima lezione.

Pubblicitร