Назад к блогу

Добавление аналитики с приоритетом конфиденциальности в приложение SvelteKit

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

SvelteKit 2 молча сломал большинство существующих интеграций аналитики. Обычный тег <script>, который прекрасно работал в SvelteKit 1, в SvelteKit 2 будет срабатывать только при жёстких перезагрузках, пропуская каждую клиентскую навигацию. Стандартное исправление — подмена history.pushState — также не работает, потому что SvelteKit перехватывает навигацию на уровне маршрутизатора до того, как вызывается pushState. Трекер Monoid корректно обрабатывает это из коробки.

Почему обычный подход не работает

Традиционные скрипты аналитики обнаруживают изменения маршрута, подменяя history.pushState и history.replaceState. Маршрутизатор SvelteKit вызывает свои внутренние примитивы навигации и вызывает pushState только как побочный эффект, после того как переход маршрута уже начался. К моменту срабатывания подменённого pushState новый маршрут может ещё не быть полностью отрендерен, и в некоторых типах навигации (программные вызовы goto(), клики <a> с предзагрузкой) тайминг производит дубликаты или пропущенные события.

Это корневая причина широко распространённой проблемы, когда Google Analytics GA4 записывает только первый просмотр страницы при начальной загрузке в приложениях SvelteKit 2.

Правильный подход

Добавьте скрипт трекера Monoid один раз в ваш корневой +layout.svelte, используя <svelte:head>:

<svelte:head>
  <script
    async
    src="https://api.monoid.website/tracker.min.js"
    data-site-id="YOUR_SITE_ID"
  ></script>
</svelte:head>

Встроенный слушатель DOMContentLoaded трекера срабатывает при начальной загрузке страницы. Для последующих клиентских навигаций хук history.pushState трекера срабатывает после того, как разрешилась внутренняя навигация SvelteKit — это означает, что каждый маршрут, включая первый, отслеживается ровно один раз без дубликатов или пропущенных событий.

Чтобы проверить, откройте вкладку Network в DevTools и наблюдайте за POST-запросами на /collect. Вы должны видеть один запрос на каждое изменение маршрута.

Разделение staging-трафика

Проекты SvelteKit обычно выполняются на localhost во время разработки. Зарегистрируйте отдельный сайт и привяжите его site_id к переменной окружения, чтобы трафик разработки никогда не загрязнял метрики продакшена:

<script>
  const siteId = import.meta.env.VITE_MONOID_SITE_ID
</script>

<svelte:head>
  {#if siteId}
    <script
      async
      src="https://api.monoid.website/tracker.min.js"
      data-site-id={siteId}
    ></script>
  {/if}
</svelte:head>

Установите VITE_MONOID_SITE_ID в .env.local для разработки (или оставьте её неустановленной, чтобы подавить отслеживание) и в вашей среде развёртывания для продакшена. Блок {#if siteId} гарантирует, что трекер никогда не внедряется, когда переменная не установлена.

Что вам не нужно

Никакого баннера согласия на cookie. Никакого обновления политики cookie. Никакой проверки navigator.cookieEnabled. Конечная точка сбора выводит ежедневный хэш посетителя из IP-адреса, User-Agent, серверного секрета и текущей даты:

visitor_hash = SHA-256(IP + UA + SALT_SECRET + YYYY-MM-DD)

Хэш сбрасывается каждые 24 часа и не может быть обращён для восстановления IP или User-Agent. На устройстве посетителя ничего не сохраняется. Это означает, что интеграция не запускает требования согласия ePrivacy или PECR — соглашаться не с чем.

Проверка в дашборде

После развёртывания откройте свой дашборд аналитики и перейдите между несколькими маршрутами на вашем живом сайте. Каждая навигация должна производить новую запись просмотра страницы с правильным путём. Если вы видите только одну запись независимо от навигации, проверьте, что скрипт загружен в корневом макете и что VITE_MONOID_SITE_ID установлен в среде продакшена.

Адаптер SvelteKit, который вы используете (Cloudflare, Vercel, Node, статический), не влияет на сбор аналитики, поскольку отслеживание происходит полностью в браузере после гидратации.