Ленивая инициализация в setup

Ленивая инициализация — это техника, позволяющая откладывать создание значений до момента их реального использования. В контексте Vue 3 и Composition API это особенно актуально при работе с setup, где некоторые вычисления могут быть дорогостоящими или зависеть от состояния, которое ещё не сформировано.

Использование функций для ленивой инициализации

В setup переменные обычно создаются с помощью ref или reactive. При стандартной инициализации значение создается сразу при вызове setup. Ленивая инициализация реализуется через функцию-обёртку:

import { ref } from 'vue';

export default {
  setup() {
    const expensiveValue = ref(null);

    function getExpensiveValue() {
      if (expensiveValue.value === null) {
        expensiveValue.value = computeExpensiveValue();
      }
      return expensiveValue.value;
    }

    function computeExpensiveValue() {
      // сложные вычисления
      return Array.from({ length: 10000 }, (_, i) => i).reduce((a, b) => a + b, 0);
    }

    return {
      expensiveValue,
      getExpensiveValue
    };
  }
}

В этом примере значение expensiveValue не вычисляется сразу, а только при первом обращении через getExpensiveValue. Такой подход снижает нагрузку на рендер и экономит ресурсы.

Ленивая инициализация с computed

computed свойства также могут использоваться для ленивой инициализации. Они создаются только при первом обращении к их значению:

import { ref, computed } from 'vue';

export default {
  setup() {
    const numbers = ref([1, 2, 3, 4, 5]);

    const sum = computed(() => {
      console.log('Вычисление суммы');
      return numbers.value.reduce((a, b) => a + b, 0);
    });

    return {
      numbers,
      sum
    };
  }
}

До первого обращения к sum.value функция редуцирования не выполнится. Этот принцип позволяет откладывать тяжелые вычисления до момента реального использования данных.

Ленивая инициализация с асинхронными данными

При работе с API или длительными асинхронными операциями ленивую инициализацию можно комбинировать с ref и watchEffect или с функциями загрузки данных по требованию:

import { ref } from 'vue';

export default {
  setup() {
    const data = ref(null);

    async function loadData() {
      if (!data.value) {
        const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
        data.value = await response.json();
      }
      return data.value;
    }

    return {
      data,
      loadData
    };
  }
}

Такой подход предотвращает ненужные сетевые запросы до тех пор, пока данные реально не понадобятся.

Ленивая инициализация в тестах с Vue Test Utils

При написании тестов с Vue Test Utils ленивость играет важную роль, так как позволяет уменьшить избыточное создание компонентов и данных. Использование shallowMount вместе с функциями для ленивой инициализации позволяет проверять поведение компонентов без дорогостоящей загрузки всех зависимостей.

import { shallowMount } from '@vue/test-utils';
import MyComponent from '@/components/MyComponent.vue';

describe('MyComponent', () => {
  it('лениво инициализирует данные', async () => {
    const wrapper = shallowMount(MyComponent);
    
    expect(wrapper.vm.expensiveValue).toBe(null);
    
    const value = wrapper.vm.getExpensiveValue();
    expect(value).toBeGreaterThan(0);
    expect(wrapper.vm.expensiveValue).toBe(value);
  });
});

Такой тест демонстрирует, что до вызова метода getExpensiveValue значение не создается, а после вызова — инициализируется корректно.

Преимущества ленивой инициализации

  • Снижение нагрузки на рендер — тяжелые вычисления выполняются только при необходимости.
  • Оптимизация памяти — данные создаются по требованию, не занимая ресурсы заранее.
  • Контроль асинхронных операций — API-запросы и тяжелые операции могут быть отложены.
  • Упрощение тестирования — можно тестировать отдельные части компонента без полной инициализации.

Ленивая инициализация является важным инструментом при работе с Composition API. Она сочетает в себе чистоту кода, эффективность и гибкость при управлении состоянием компонента и его вычисляемыми свойствами.