Як повторно використовувати логіку у Vue.js за допомогою Composables

У процесі розробки програмного забезпечення ключовим моментом є організація коду таким чином, щоб забезпечити його повторне використання. Дублювання кодових фрагментів може призвести до розширення кодової бази та ускладнення процесу налагодження, особливо в масштабних проєктах.

Vue пропонує елегантний механізм для повторного використання коду через композиційні функції (composables). Ці функції є своєрідними “капсулами” логіки, які можна використовувати повторно в різних частинах проєкту для реалізації схожих функціональних можливостей.

Чи завжди були Composables?

До появи Composables у Vue 3, для повторного використання коду застосовувалися міксини (mixins). Міксини дозволяли включати до компонента Vue.js різноманітні опції, такі як дані, методи та хуки життєвого циклу, що дозволяло ділитись кодом між різними компонентами.

Міксини організовувались в окремі файли, а потім інтегрувались до компонентів шляхом додавання їх до властивості `mixins` в об’єкті параметрів компонента. Ось приклад:

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;
    },
  },
};

У цьому прикладі показано міксин для валідації форми. Він включає дві властивості даних: `formData` та `formErrors`, ініціалізовані пустими значеннями.

`formData` зберігає дані, введені у форму, зокрема поля імені користувача та пароля, які спочатку порожні. `formErrors` призначений для зберігання повідомлень про помилки, також починаючи з порожніх значень.

Міксин також містить метод `validateForm()`, який перевіряє, чи не порожні поля імені користувача та пароля. Якщо якесь із полів порожнє, у властивість `formErrors` додається відповідне повідомлення про помилку.

Метод повертає `true`, якщо форма коректна, тобто коли `formErrors` порожній. Міксин можна використовувати, імпортувавши його в компонент Vue і додавши до властивості `mixin` об’єкта `Options`:

<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>

Цей приклад демонструє Vue компонент, розроблений з використанням підходу об’єкта `Options`. Властивість `mixins` включає всі імпортовані міксини. У цьому випадку компонент використовує метод `validateForm` з міксину `formValidation`, щоб повідомити користувача про успішне відправлення форми.

Як використовувати Composables

Компонований файл – це самостійний JavaScript-файл з функціями, розробленими для конкретних задач чи вимог. Усередині composable можна використовувати Composition API від Vue, наприклад, функції `ref` і `computed`.

Такий доступ до Composition API дозволяє створювати функції, які легко інтегруються в різні компоненти. Ці функції повертають об’єкт, який можна легко імпортувати та використовувати у Vue компонентах через функцію `setup` Composition API.

Щоб почати використовувати composable, створіть новий JavaScript файл у каталозі `src` вашого проекту. Для великих проектів рекомендується створити окрему папку в `src` і розміщувати в ній окремі JavaScript файли для різних composables, причому імена файлів повинні відображати їх призначення.

У цьому файлі JavaScript визначте потрібну функцію. Ось реструктуризація міксину `formValidation` у 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,
  };
}

Цей код починається з імпорту функції `reactive` з пакета `vue`. Потім створюється функція `useFormValidation()`, яка призначена для експорту.

Далі створюється реактивна змінна `state`, яка містить властивості `formData` та `formErrors`. Потім виконується валідація форми, дуже схожа на підхід у міксині. Наприкінці, повертається об’єкт зі змінною `state` та функцією `validateForm`.

Використовувати цей composable можна, імпортувавши його функцію JavaScript у вашому компоненті:

<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>

Після імпорту composable `useFormValidation`, код деструктуризує JavaScript об’єкт, який повертається, і продовжує валідацію форми. Він виводить повідомлення про те, чи успішно відправлена форма, або про наявність помилок.

Composables – це нові міксини

Міксини були корисні у Vue 2 для повторного використання коду, але у Vue 3 їхню роль перейняли composables. Composables надають більш структурований та зручний підхід для повторного використання логіки у Vue.js додатках, що спрощує розробку масштабованих веб-програм з використанням Vue.