Миграция с Options API

Vue.js изначально использует Options API, где структура компонента определяется через объект с ключами data, methods, computed, watch и т.д. Composition API позволяет группировать логику по функциональности, а не по типу опций, что улучшает читаемость и повторное использование кода.


Переписывание data и реактивность

В Options API data возвращает объект с состоянием:

export default {
  data() {
    return {
      count: 0,
      message: 'Привет'
    };
  }
}

В Composition API реактивные свойства создаются с помощью ref и reactive:

import { ref, reactive } from 'vue';

export default {
  setup() {
    const count = ref(0);
    const state = reactive({ message: 'Привет' });

    return { count, state };
  }
}

Ключевые моменты:

  • ref используется для примитивных типов.
  • reactive — для объектов и массивов.
  • Для доступа к значению ref используется .value внутри setup.

Методы и вычисляемые свойства

В Options API методы объявляются в объекте methods, а вычисляемые свойства — в computed:

export default {
  data() { return { count: 0 }; },
  methods: {
    increment() {
      this.count++;
    }
  },
  computed: {
    double() {
      return this.count * 2;
    }
  }
}

В Composition API для этого используются функции computed и обычные функции внутри setup:

import { ref, computed } from 'vue';

export default {
  setup() {
    const count = ref(0);
    const increment = () => { count.value++; };
    const double = computed(() => count.value * 2);

    return { count, increment, double };
  }
}

Особенности миграции:

  • this больше не используется внутри setup.
  • Вычисляемые свойства создаются через computed с явным возвратом.

Следящие свойства (watch)

Options API:

export default {
  data() { return { count: 0 }; },
  watch: {
    count(newValue, oldValue) {
      console.log(newValue, oldValue);
    }
  }
}

Composition API:

import { ref, watch } from 'vue';

export default {
  setup() {
    const count = ref(0);
    watch(count, (newValue, oldValue) => {
      console.log(newValue, oldValue);
    });

    return { count };
  }
}

Примечания:

  • watch может следить за реактивными объектами, отдельными ref или функциями.
  • Позволяет более гибко настраивать наблюдение, включая immediate и deep.

Логика жизненного цикла

Options API использует хуки, такие как created, mounted, beforeUnmount:

export default {
  mounted() {
    console.log('Компонент смонтирован');
  }
}

В Composition API используются функции из Vue:

import { onMounted, onUnmounted } from 'vue';

export default {
  setup() {
    onMounted(() => {
      console.log('Компонент смонтирован');
    });

    onUnmounted(() => {
      console.log('Компонент будет удалён');
    });
  }
}

Особенности:

  • Можно объединять несколько функций одного типа для разных реактивных данных.
  • Позволяет группировать логику жизненного цикла вместе с данными и методами.

Передача пропсов и использование emit

Options API:

export default {
  props: ['title'],
  methods: {
    sendEvent() {
      this.$emit('customEvent', this.title);
    }
  }
}

Composition API:

export default {
  props: ['title'],
  setup(props, { emit }) {
    const sendEvent = () => emit('customEvent', props.title);

    return { sendEvent };
  }
}

Особенности:

  • props доступен напрямую в setup.
  • emit передаётся вторым аргументом деструктурированным из контекста.

Организация логики по функциональности

Главное преимущество Composition API — возможность группировать код по функциональности, а не по типу:

Options API:

export default {
  data() { return { count: 0 }; },
  methods: { increment() { this.count++; } },
  computed: { double() { return this.count * 2; } },
  watch: { count(newVal) { console.log(newVal); } }
}

Composition API:

import { ref, computed, watch } from 'vue';

export default {
  setup() {
    // Счётчик
    const count = ref(0);
    const increment = () => { count.value++; };
    const double = computed(() => count.value * 2);
    watch(count, newVal => console.log(newVal));

    return { count, increment, double };
  }
}

Преимущества:

  • Логика компонентов легко читается и переиспользуется.
  • Можно вынести функциональные блоки в отдельные функции (composables).

Использование composables для повторного кода

Создание переиспользуемой функции:

// useCounter.js
import { ref, computed } from 'vue';

export function useCounter(initial = 0) {
  const count = ref(initial);
  const increment = () => { count.value++; };
  const double = computed(() => count.value * 2);

  return { count, increment, double };
}

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

import { useCounter } from './useCounter';

export default {
  setup() {
    const { count, increment, double } = useCounter(5);
    return { count, increment, double };
  }
}

Ключевые моменты:

  • Упрощает тестирование и поддержку.
  • Позволяет разделять логику и UI-компоненты.

Миграция шаблонов

В большинстве случаев шаблоны остаются без изменений, но:

  • Доступ к ref требует .value в JS, но в шаблоне Vue автоматически разыменовывает.
  • Все методы, вычисляемые свойства и реактивные данные должны возвращаться из setup.
<template>
  <div>
    <p>{{ count }}</p>
    <p>{{ double }}</p>
    <button @click="increment">Увеличить</button>
  </div>
</template>

Рекомендации по этапам миграции

  1. Начать с отдельных компонентов.
  2. Постепенно переписывать data, methods и computed.
  3. Переносить watch и жизненные циклы.
  4. Выносить повторяющиеся блоки в composables.
  5. Проверять корректность шаблонов и реактивности после каждого шага.