Кеширование данных маршрутов

Navigo — это легковесный роутер для JavaScript, предназначенный для одностраничных приложений (SPA). Одной из важных задач при работе с маршрутами является оптимизация производительности через кеширование данных, загружаемых при переходах между маршрутами. Кеширование позволяет сократить количество сетевых запросов, ускорить отображение страниц и уменьшить нагрузку на сервер.


Подходы к кешированию

В контексте Navigo кеширование может применяться на нескольких уровнях:

  1. Кеширование результатов сетевых запросов Часто маршруты загружают данные через API. Вместо повторного запроса при каждом переходе можно сохранять результат в объекте или Map, связав его с ключом маршрута.

    const routeCache = new Map();
    
    function fetchData(routeKey, fetchFunction) {
        if (routeCache.has(routeKey)) {
            return Promise.resolve(routeCache.get(routeKey));
        }
        return fetchFunction().then(data => {
            routeCache.set(routeKey, data);
            return data;
        });
    }
    
    router.on('/users', () => {
        fetchData('users', () => fetch('/api/users').then(res => res.json()))
            .then(users => renderUsers(users));
    });

    В этом примере данные пользователей кешируются после первого запроса. Последующие переходы на /users используют кеш.

  2. Кеширование шаблонов и компонентов Если приложение использует динамические компоненты или шаблоны, их можно хранить в памяти после первого рендера. Это особенно полезно при использовании client-side rendering.

    const templateCache = {};
    
    function loadTemplate(name, fetchTemplate) {
        if (!templateCache[name]) {
            templateCache[name] = fetchTemplate();
        }
        return templateCache[name];
    }
    
    router.on('/dashboard', () => {
        const template = loadTemplate('dashboard', () => fetch('/templates/dashboard.html').then(res => res.text()));
        template.then(html => render(html));
    });

    Такой подход снижает задержку при повторном рендере страниц.


Стратегии управления кешем

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

  • TTL (Time to Live) — хранение данных только ограниченное время:

    const cacheTTL = new Map();
    
    function setCacheWithTTL(key, value, ttl = 60000) {
        cacheTTL.set(key, { value, expiry: Date.now() + ttl });
    }
    
    function getCacheWithTTL(key) {
        const cached = cacheTTL.get(key);
        if (cached && cached.expiry > Date.now()) {
            return cached.value;
        }
        cacheTTL.delete(key);
        return null;
    }
  • Ленивое удаление (Lazy Eviction) — удаление устаревших данных при следующем доступе к кешу.

  • LRU (Least Recently Used) — хранение ограниченного количества элементов, удаляя наименее используемые.


Интеграция кеша с Navigo

Navigo предоставляет возможность обрабатывать маршруты через функции-обработчики. Кеш можно интегрировать на этапе вызова обработчика маршрута или загрузки данных:

router.on('/products/:id', (params) => {
    const key = `product_${params.id}`;
    fetchData(key, () => fetch(`/api/products/${params.id}`).then(res => res.json()))
        .then(product => renderProduct(product));
});

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


Кеширование для маршрутов с параметрами

Маршруты с динамическими параметрами требуют генерации уникальных ключей кеша. Например:

  • /posts/1post_1
  • /posts/2post_2
function getRouteKey(route, params) {
    return `${route}_${Object.values(params).join('_')}`;
}

router.on('/posts/:postId', (params) => {
    const key = getRouteKey('posts', params);
    fetchData(key, () => fetch(`/api/posts/${params.postId}`).then(res => res.json()))
        .then(post => renderPost(post));
});

Такой подход предотвращает перезапись кеша для разных параметров одного маршрута.


Асинхронное кеширование и предварительная загрузка

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

function preloadRouteData(routeKey, fetchFunction) {
    fetchFunction().then(data => routeCache.set(routeKey, data));
}

// Предварительная загрузка списка товаров
preloadRouteData('products', () => fetch('/api/products').then(res => res.json()));

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


Комбинирование кеширования и обновления данных

Иногда важно показывать свежие данные, но при этом использовать кеш для скорости. Эту задачу решают стратегии stale-while-revalidate:

  1. Сначала возвращается кешированное значение.
  2. Параллельно выполняется сетевой запрос.
  3. Если данные обновились, вызывается ререндер страницы.
function fetchWithRevalidate(key, fetchFunction, render) {
    const cached = routeCache.get(key);
    if (cached) render(cached);

    fetchFunction().then(data => {
        routeCache.set(key, data);
        if (!cached) render(data); // Если данных не было в кеше, рендерим сразу
        else render(data); // Если были устаревшие данные, обновляем
    });
}

Этот подход обеспечивает баланс между производительностью и актуальностью информации.


Оптимизация памяти и очистка кеша

Для долгоживущих приложений важно контролировать размер кеша:

  • Ограничение числа элементов через Map с контролем размера.
  • Удаление данных при выходе из маршрута, если они больше не нужны.
  • Использование WeakMap для объектов, которые могут быть собраны сборщиком мусора автоматически.

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

  • Всегда генерировать уникальные ключи для динамических маршрутов.
  • Комбинировать ленивое и TTL-кеширование для контроля памяти.
  • Использовать предзагрузку для маршрутов, доступ к которым высоко вероятен.
  • Применять stale-while-revalidate для балансировки скорости и актуальности данных.
  • Разделять кеш данных и кеш шаблонов/компонентов для упрощения управления.

Эффективное кеширование маршрутов в Navigo существенно ускоряет SPA и повышает отзывчивость приложения без изменения структуры роутера.