Мемоизация вычислений

Мемоизация — это техника оптимизации, при которой результаты вычислений сохраняются для последующего использования, что предотвращает повторное выполнение ресурсоёмких операций. В контексте Quasar Framework и Vue.js мемоизация особенно полезна при работе с вычисляемыми свойствами, сложными фильтрами и компонентами, зависящими от больших массивов данных.

В Vue 3, на котором базируется Quasar, мемоизация реализуется через computed и watchEffect, а также с помощью сторонних утилит, таких как lodash.memoize. Главная цель — хранить результаты функций и возвращать их при одинаковых входных данных, минимизируя нагрузку на реактивную систему.


Вычисляемые свойства и мемоизация

В Quasar вычисляемые свойства (computed) автоматически кэшируют результат до тех пор, пока не изменятся их зависимости:

import { ref, computed } from 'vue';

const numbers = ref([1, 2, 3, 4, 5]);

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

В этом примере, даже если к sum обращаются несколько раз в шаблоне, функция reduce выполняется только тогда, когда изменяется numbers.value. Ключевой момент: реактивная зависимость определяет, когда кэш нужно сбросить.


Кэширование функций с lodash.memoize

Для функций, которые не являются вычисляемыми свойствами, мемоизацию можно реализовать через lodash.memoize:

import memoize from 'lodash.memoize';

const heavyCalculation = (num) => {
  console.log('Выполняем тяжёлое вычисление');
  return num * 2;
};

const memoizedCalculation = memoize(heavyCalculation);

console.log(memoizedCalculation(5)); // Выполняется функция
console.log(memoizedCalculation(5)); // Берётся из кэша

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


Мемоизация и компоненты Quasar

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

import { computed, ref } from 'vue';
import memoize from 'lodash.memoize';

const items = ref([
  { name: 'Alice', age: 30 },
  { name: 'Bob', age: 25 },
]);

const filterByAge = memoize((age) => {
  console.log('Фильтруем по возрасту');
  return items.value.filter(item => item.age >= age);
});

const filteredItems = computed(() => filterByAge(28));

Здесь функция filterByAge выполняется один раз для каждого уникального значения age. При повторном использовании того же аргумента результат берётся из кэша.


Мемоизация с реактивными объектами

При работе с реактивными объектами и массивами следует учитывать, что мемоизация по ссылке может не сработать, если объект изменился. В таких случаях используют глубокое клонирование или сериализацию аргументов:

const deepMemoize = (fn) => {
  const cache = new Map();
  return (arg) => {
    const key = JSON.stringify(arg);
    if (cache.has(key)) return cache.get(key);
    const result = fn(arg);
    cache.set(key, result);
    return result;
  };
};

const processData = deepMemoize((data) => data.map(x => x * 2));

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


Мемоизация и асинхронные вычисления

Для асинхронных функций мемоизация требует отдельного подхода, так как возвращается промис:

import memoize from 'lodash.memoize';

const fetchData = async (id) => {
  const response = await fetch(`/api/data/${id}`);
  return response.json();
};

const memoizedFetch = memoize(async (id) => fetchData(id));

memoizedFetch(1).then(data => console.log(data));
memoizedFetch(1).then(data => console.log(data)); // Берётся из кэша

Важно: кэш хранит промис, а не его результат, что предотвращает повторные запросы при одновременных вызовах.


Практические рекомендации

  • Использовать мемоизацию для дорогих вычислений, особенно при работе с массивами и объектами большого объёма.
  • Для реактивных объектов проверять корректность ключей кэша и учитывать изменение ссылок.
  • Не мемоизировать лёгкие функции без значительной нагрузки — кэширование имеет собственные накладные расходы.
  • В компонентах Quasar оптимизировать таблицы, списки и фильтры через вычисляемые свойства и мемоизированные функции.
  • Для асинхронных запросов мемоизация уменьшает количество сетевых обращений и ускоряет интерфейс.

Мемоизация в Quasar сочетает возможности Vue 3 реактивности и стандартные методы кэширования, создавая гибкую систему оптимизации вычислений. Ее правильное использование значительно повышает производительность приложений без снижения читаемости кода.