Динамическое обновление meta

В одностраничных приложениях (SPA) маршрутизация происходит без полной перезагрузки страницы. Это означает, что стандартные мета-теги (<title>, <meta name="description">, <meta property="og:*">) остаются неизменными, если не обновлять их вручную. Библиотека Navigo предоставляет удобный механизм для обработки маршрутов, но управление meta-данными ложится на разработчика.

Динамическое обновление meta необходимо для:

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

Базовая работа с Navigo и маршрутизацией

Создание маршрутизатора:

import Navigo from 'navigo';

const router = new Navigo('/', { hash: true });

router
  .on('/home', () => renderPage('home'))
  .on('/about', () => renderPage('about'))
  .resolve();

Функция renderPage отвечает за отрисовку контента. Именно здесь удобно интегрировать обновление meta-данных.


Обновление заголовка страницы

Самый простой элемент — <title>. Обновляется напрямую через DOM:

function updateTitle(title) {
  document.title = title;
}

Использование в маршрутах:

router.on('/about', () => {
  renderPage('about');
  updateTitle('О компании');
});

Управление meta description

Meta description влияет на сниппеты в поисковой выдаче:

function updateDescription(description) {
  let tag = document.querySelector('meta[name="description"]');

  if (!tag) {
    tag = document.createElement('meta');
    tag.setAttribute('name', 'description');
    document.head.appendChild(tag);
  }

  tag.setAttribute('content', description);
}

Применение:

router.on('/home', () => {
  renderPage('home');
  updateDescription('Главная страница сайта');
});

Обновление Open Graph тегов

Для социальных сетей важны теги og:title, og:description, og:image:

function updateMetaProperty(property, content) {
  let tag = document.querySelector(`meta[property="${property}"]`);

  if (!tag) {
    tag = document.createElement('meta');
    tag.setAttribute('property', property);
    document.head.appendChild(tag);
  }

  tag.setAttribute('content', content);
}

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

router.on('/post/:id', ({ data }) => {
  const post = getPost(data.id);

  renderPost(post);

  updateMetaProperty('og:title', post.title);
  updateMetaProperty('og:description', post.summary);
  updateMetaProperty('og:image', post.image);
});

Централизация meta-логики

Для упрощения поддержки удобно создать единый слой управления:

function updateMeta(meta) {
  if (meta.title) {
    document.title = meta.title;
  }

  if (meta.description) {
    updateDescription(meta.description);
  }

  if (meta.og) {
    Object.entries(meta.og).forEach(([key, value]) => {
      updateMetaProperty(`og:${key}`, value);
    });
  }
}

Маршруты становятся чище:

router.on('/about', () => {
  renderPage('about');

  updateMeta({
    title: 'О компании',
    description: 'Информация о компании',
    og: {
      title: 'О компании',
      description: 'Узнайте больше о нас',
      image: '/images/about.jpg'
    }
  });
});

Использование middleware в Navigo

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

router.hooks({
  before: (done, match) => {
    const routeMeta = getRouteMeta(match.url);

    if (routeMeta) {
      updateMeta(routeMeta);
    }

    done();
  }
});

Функция getRouteMeta может возвращать заранее описанные meta-данные:

const metaConfig = {
  '/home': {
    title: 'Главная',
    description: 'Добро пожаловать'
  },
  '/about': {
    title: 'О нас',
    description: 'Информация о компании'
  }
};

function getRouteMeta(url) {
  return metaConfig[url];
}

Динамические маршруты и meta

Для маршрутов с параметрами требуется генерация данных:

router.on('/product/:id', ({ data }) => {
  const product = getProduct(data.id);

  renderProduct(product);

  updateMeta({
    title: product.name,
    description: product.description,
    og: {
      title: product.name,
      description: product.description,
      image: product.image
    }
  });
});

Асинхронные данные

Если meta зависит от API, важно обновлять их после загрузки:

router.on('/article/:id', async ({ data }) => {
  const article = await fetchArticle(data.id);

  renderArticle(article);

  updateMeta({
    title: article.title,
    description: article.excerpt
  });
});

Очистка и предотвращение конфликтов

При частом обновлении важно избегать дублирования тегов:

  • всегда искать существующий тег перед созданием
  • не создавать одинаковые meta с разными значениями
  • контролировать уникальность property и name

Интеграция с SSR и pre-rendering

SPA-приложения часто используют:

  • server-side rendering
  • pre-rendering (например, для SEO)

В таких случаях:

  • начальные meta задаются сервером
  • Navigo обновляет их при навигации

Важно не конфликтовать с серверной разметкой:

// не создавать новый тег, если он уже есть
document.querySelector('meta[name="description"]');

Частые ошибки

Отсутствие обновления title

  • страница меняется, заголовок остаётся прежним

Дублирование meta-тегов

  • несколько <meta name="description">

Игнорирование Open Graph

  • некорректное отображение ссылок в соцсетях

Синхронное обновление при асинхронных данных

  • meta обновляется до получения данных

Оптимизация и расширение

Дополнительные возможности:

  • кеширование meta для уже посещённых маршрутов
  • генерация meta на основе шаблонов
  • локализация meta-данных
  • интеграция с аналитикой

Пример шаблона:

function generateMeta(template, data) {
  return template.replace(/\{\{(.*?)\}\}/g, (_, key) => data[key.trim()]);
}

Архитектурный подход

Рекомендуется выделить отдельный модуль:

/meta
  ├── updateMeta.js
  ├── metaConfig.js
  └── helpers.js

Это позволяет:

  • централизовать управление
  • упростить тестирование
  • переиспользовать логику

Практическая схема работы

  1. Navigo определяет маршрут
  2. Получаются данные (если нужно)
  3. Рендерится контент
  4. Обновляются meta-теги

Чёткое соблюдение этой последовательности исключает рассинхронизацию между содержимым страницы и её мета-данными.