Gjenbruk Vue.js-kode effektivt med Composables!

I programmering er det grunnleggende å organisere kode på en måte som fremmer gjenbruk. Duplisering av kode kan føre til at kodebasen vokser seg stor og vanskelig å håndtere, spesielt i omfattende applikasjoner.

Vue forenkler kode gjenbruk med hjelp av *composables*. Dette er funksjoner som samler sammen logikk, og kan benyttes på nytt i hele prosjektet for å håndtere repeterende oppgaver.

Var det alltid Composables?

Før introduksjonen av *composables* i Vue 3, var det vanlig å benytte *mixins* for å oppnå kode gjenbruk i ulike deler av applikasjonen. *Mixins* inneholdt Vue.js-spesifikke elementer som data, metoder, og livssyklus-hooks, som tillot gjenbruk av kode i flere komponenter.

For å opprette en *mixin*, organiserte man koden i separate filer som deretter ble brukt i komponenter ved å legge til *mixin* til *mixins*-egenskapen i komponentens *options* objekt. For eksempel:

 
export const formValidationMixin = {
  data() {
    return {
      formData: {
        username: '',
        password: '',
      },
      formErrors: {
        username: '',
        password: '',
      },
    };
  },
  methods: {
    validateForm() {
      this.formErrors = {};
  
      if (!this.formData.username.trim()) {
        this.formErrors.username="Username is required.";
      }
  
      if (!this.formData.password.trim()) {
        this.formErrors.password = 'Password is required.';
      }
    
      return Object.keys(this.formErrors).length === 0;
    },
  },
};

Dette kodeutdraget demonstrerer en *mixin* for skjema validering. Den inkluderer to dataegenskaper, `formData` og `formErrors`, som innledningsvis er satt til tomme verdier.

`formData` lagrer brukerinndata fra skjemaet, som brukernavn og passord. `formErrors` har en lignende struktur for å lagre feilmeldinger og er også tom fra starten.

Videre definerer *mixin* en metode, `validateForm()`, for å verifisere at både brukernavn- og passordfeltene inneholder verdi. Hvis et felt er tomt, fylles `formErrors` med en tilhørende feilmelding.

Metoden returnerer `true` om skjemaet er gyldig og `formErrors` er tom. For å benytte *mixin*, importeres den til Vue-komponenten og inkluderes i *mixin* egenskapen av Options-objektet:

 <template>
  <div>
    <form @submit.prevent="submitForm">
      <div>
        <label for="username">Username:</label>
        <input type="text" id="username" v-model="formData.username" />
        <span class="error">{{ formErrors.username }}</span>
      </div>
      <div>
        <label for="password">Password:</label>
        <input type="password" id="password" v-model="formData.password" />
        <span class="error">{{ formErrors.password }}</span>
      </div>
      <button type="submit">Submit</button>
    </form>
  </div>
</template>

<script>
import { formValidation } from "./formValidation.js";

export default {
  mixins: [formValidation],
  methods: {
    submitForm() {
      if (this.validateForm()) {
        alert("Form submitted successfully!");
      } else {
        alert("Please correct the errors in the form.");
      }
    },
  },
};
</script>

<style>
.error {
  color: red;
}
</style>

Dette eksempelet viser en Vue-komponent opprettet med *Options API*. *Mixins* -egenskapen inneholder alle importerte mixins. I dette tilfellet benytter komponenten `validateForm` metoden fra *formValidation* mixin for å informere brukeren om utfallet av skjemainnsendingen.

Hvordan benytte Composables

En *composable* er en uavhengig JavaScript-fil med funksjoner som er spesifikt laget for å løse et bestemt behov. I en *composable* kan du utnytte Vue’s *Composition API* og benytte funksjoner som `refs` og `computed refs`.

Med tilgang til Composition API, kan du opprette funksjoner som kan benyttes i flere komponenter. Disse funksjonene returnerer et objekt, som enkelt kan importeres og benyttes i Vue-komponenter gjennom `setup`-funksjonen til Composition API.

For å ta i bruk en *composable*, opprett en ny JavaScript-fil i prosjektets `src`-katalog. I større prosjekter, bør du vurdere å organisere filene i en egen mappe, og lage separate JavaScript-filer for hver *composable* for å tydeliggjøre funksjonaliteten.

I JavaScript-filen definerer du den nødvendige funksjonen. Nedenfor er en restrukturering av *formValidation*-mixinen som en *composable*:

 
import { reactive } from 'vue';

export function useFormValidation() {
  const state = reactive({
    formData: {
      username: '',
      password: '',
    },
    formErrors: {
      username: '',
      password: '',
    },
  });

  function validateForm() {
    state.formErrors = {};

    if (!state.formData.username.trim()) {
      state.formErrors.username="Username is required.";
    }

    if (!state.formData.password.trim()) {
      state.formErrors.password = 'Password is required.';
    }

    return Object.keys(state.formErrors).length === 0;
  }

  return {
    state,
    validateForm,
  };
}

Dette kodeutdraget begynner med å importere `reactive` funksjonen fra *vue*-pakken. Deretter opprettes en eksporterbar funksjon, `useFormValidation()`.

Videre opprettes en reaktiv variabel, `state`, som inneholder egenskapene `formData` og `formErrors`. Koden håndterer deretter skjemavalideringen på en lignende måte som mixinen. Til slutt returneres `state`-variabelen og `validateForm` funksjonen som et objekt.

Du kan ta i bruk denne *composable* ved å importere JavaScript-funksjonen fra filen i komponenten din:

 <template>
  <div>
    <form @submit.prevent="submitForm">
      <div>
        <label for="username">Username:</label>
        <input type="text" id="username" v-model="state.formData.username" />
        <span class="error">{{ state.formErrors.username }}</span>
      </div>
      <div>
        <label for="password">Password:</label>
        <input type="password" id="password" v-model="state.formData.password" />
        <span class="error">{{ state.formErrors.password }}</span>
      </div>
      <button type="submit">Submit</button>
    </form>
  </div>
</template>

<script setup>
import { useFormValidation } from "./formValidation.js";
import { ref } from "vue";
const { state, validateForm } = useFormValidation();

const submitForm = () => {
  if (validateForm()) {
    alert("Form submitted successfully!");
  } else {
    alert("Please correct the errors in the form.");
  }
};
</script>

<style>
.error {
  color: red;
}
</style>

Etter å ha importert `useFormValidation` *composable*, destructuring koden JavaScript-objektet den returnerer og fortsetter skjemavalideringen. Den varsler om skjemaet er vellykket innsendt eller om det er feil.

Composables er de nye Mixins

Mens *mixins* var nyttige i Vue 2 for kode gjenbruk, har *composables* tatt deres plass i Vue 3. *Composables* tilbyr en mer strukturert og vedlikeholdsvennlig tilnærming for gjenbruk av logikk i Vue.js applikasjoner, som gjør det enklere å skape skalerbare web-applikasjoner med Vue.