Создание композаблов

Композаблы (composables) в Quasar основаны на концепции Composition API из Vue 3 и представляют собой функции, которые инкапсулируют реактивную логику, состояния и методы для многократного использования в различных компонентах. Они позволяют структурировать код модульно, улучшая читаемость и поддерживаемость проекта.

Композабл — это обычная функция, которая возвращает реактивные объекты, вычисляемые свойства или методы. Обычно она помещается в папку src/composables/ для логической организации.

// src/composables/useCounter.js
import { ref } from 'vue';

export function useCounter() {
  const count = ref(0);

  function increment() {
    count.value++;
  }

  function decrement() {
    count.value--;
  }

  return {
    count,
    increment,
    decrement
  };
}

Использование композабла в компоненте:

<template>
  <div>
    <p>Счетчик: {{ count }}</p>
    <q-btn @click="increment" label="Увеличить" />
    <q-btn @click="decrement" label="Уменьшить" />
  </div>
</template>

<script setup>
import { useCounter } from 'src/composables/useCounter';

const { count, increment, decrement } = useCounter();
</script>

Реактивные состояния и вычисляемые свойства

Композаблы позволяют хранить reactive state и вычислять зависимости между значениями. Для сложной логики часто используют комбинацию ref, reactive и computed.

import { reactive, computed } from 'vue';

export function useTodoList() {
  const state = reactive({
    todos: [],
    filter: 'all'
  });

  const filteredTodos = computed(() => {
    if (state.filter === 'completed') {
      return state.todos.filter(todo => todo.done);
    } else if (state.filter === 'active') {
      return state.todos.filter(todo => !todo.done);
    }
    return state.todos;
  });

  function addTodo(text) {
    state.todos.push({ text, done: false });
  }

  function toggleTodo(index) {
    state.todos[index].done = !state.todos[index].done;
  }

  return { state, filteredTodos, addTodo, toggleTodo };
}

Интеграция с Quasar UI-компонентами

Quasar предоставляет компоненты и утилиты, которые удобно использовать внутри композаблов. Например, Notify для уведомлений или Dialog для модальных окон. Это позволяет создать полностью независимую бизнес-логику, не зависящую от конкретного компонента.

import { Notify } from 'quasar';
import { ref } from 'vue';

export function useNotifications() {
  const messages = ref([]);

  function notifySuccess(msg) {
    Notify.create({ message: msg, color: 'green', position: 'top' });
    messages.value.push({ msg, type: 'success' });
  }

  function notifyError(msg) {
    Notify.create({ message: msg, color: 'red', position: 'top' });
    messages.value.push({ msg, type: 'error' });
  }

  return { messages, notifySuccess, notifyError };
}

Композаблы для работы с API

Частой задачей является создание композаблов для асинхронных операций, таких как запросы к серверу. Для этого можно использовать axios или встроенные методы fetch и управлять состоянием загрузки, ошибками и результатом.

import { ref } from 'vue';
import axios from 'axios';

export function useApi(url) {
  const data = ref(null);
  const loading = ref(false);
  const error = ref(null);

  async function fetchData() {
    loading.value = true;
    error.value = null;
    try {
      const response = await axios.get(url);
      data.value = response.data;
    } catch (err) {
      error.value = err;
    } finally {
      loading.value = false;
    }
  }

  return { data, loading, error, fetchData };
}

Организация и переиспользование

  1. Папка src/composables/ — каждая логическая единица должна иметь отдельный файл или подкаталог.
  2. Именование — префикс use помогает сразу понять, что это композабл: useAuth, useForm, useCart.
  3. Возврат объектов — всегда возвращать реактивные состояния и методы, необходимые внешним компонентам.
  4. Минимальные зависимости — композаблы должны быть максимально автономны, не завися от конкретного компонента.

Паттерны использования

  • UI-компоненты + бизнес-логика: разделение логики и визуальной части. Композабл управляет состоянием и методами, компонент отвечает только за отображение.
  • Многокомпонентная синхронизация: один композабл может использоваться в нескольких местах, сохраняя общий стейт или методы.
  • Повторное использование API-запросов: центральный композабл для CRUD-операций с сервером.

Реактивное состояние с Pinia в композаблах

Композаблы хорошо сочетаются с Pinia для глобального состояния. Можно создавать композаблы, которые инкапсулируют вызовы к Pinia-сторам, предоставляя удобный интерфейс:

import { useUserStore } from 'src/stores/user';
import { computed } from 'vue';

export function useUser() {
  const userStore = useUserStore();

  const isLoggedIn = computed(() => !!userStore.token);
  const username = computed(() => userStore.name);

  function login(credentials) {
    return userStore.login(credentials);
  }

  function logout() {
    userStore.logout();
  }

  return { isLoggedIn, username, login, logout };
}

Преимущества композаблов

  • Инкапсуляция логики: код структурирован и легко поддерживается.
  • Многократное использование: один и тот же композабл можно подключать в разных компонентах.
  • Тестируемость: функции можно тестировать отдельно от UI.
  • Совместимость с Vue 3 и Quasar: полностью интегрируются с Composition API и Quasar UI-компонентами.

Композаблы создают основу для модульного и масштабируемого кода в Quasar, позволяя строить сложные приложения с чистой архитектурой и легко управляемым состоянием.