Skip to content
  1. Главная
  2. Справочный центр
  3. Разработчикам
  4. Свои события из одностраничного приложения

Свои события из одностраничного приложения

Обновлено:

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

Что счётчик делает сам

В одностраничном приложении счётчик отслеживает смену адреса и пишет просмотр на каждый переход между экранами. Визиты, источники, кампании, устройство и посетитель приезжают оттуда же. Настраивать это не нужно, и дублировать своим кодом не нужно тем более: подписка на роутер с собственным «просмотр страницы» удваивает просмотры, а не подстраховывает.

Своим кодом отправляется только то, что счётчик увидеть не может, — действия внутри приложения. Событие ложится на визит, и вопрос «сколько раз нажали» превращается в «сколько нажали из платного трафика и во что это превратилось».

Одно место всё же стоит настроить — фильтр параметров адреса, чтобы один экран не размножился в отчёте на десяток строк из-за ?tab=2&_r=17. Объявляется до загрузки счётчика:

var ko_options = {
  modules: { visit: {
    url_tracking_hash_router: false,               // true, если маршруты через #/
    url_tracking_query_patterns: ['*', '!utm_*', '!_*'],
  } }
};

Что означают эти ключи — Вызовы счётчика.

Поставьте обёртку

Заведите одну тонкую обёртку и зовите только её. Прямой вызов счётчика из компонента — способ однажды отправить событие мимо словаря и в неправильной форме.

В Angular это сервис, в React — модуль; суть одна.

const PROD_HOST = 'app.example.com';
const COUNTER_SRC = 'https://cdn.kodata.pro/site/?hash=ВАШ_HASH';

const enabled = window.location.hostname === PROD_HOST;

export function initCounter(): void {
  if (!enabled) return;
  const script = document.createElement('script');
  script.src = COUNTER_SRC;
  script.async = true;
  document.head.appendChild(script);
}

export function track(event: string, data: Record<string, string | number | boolean> = {}): void {
  call('track', [event, { data }]);
}

export function setContext(data: Record<string, string | number | boolean>): void {
  call('setTrackData', [data, true]);
}

function call(method: 'track' | 'setTrackData', args: any[]): void {
  if (!enabled) return;
  const visit = (window as any).ko?.visit;
  if (visit && typeof visit[method] === 'function') {
    visit[method].apply(visit, args);
    return;
  }
  (window as any).koLayer = (window as any).koLayer || [];
  (window as any).koLayer.push(['visit', method, args]);
}

Подставьте свой домен в PROD_HOST и свой ключ в COUNTER_SRC — больше в этом блоке менять нечего. Форма вызова, устройство очереди koLayer и второй аргумент setTrackData разобраны в Вызовах счётчика.

Гейт по домену обязателен. Без него в проекте появятся «сайты» вроде localhost и 192.168.0.5, а числа использования наполовину будут состоять из работы разработчиков. В обёртке выше он гасит и сам счётчик, и все вызовы.

Отправьте первое событие

Возьмите действие, факт которого подтверждает сервер, и поставьте вызов в успешную ветку ответа:

track('report_exported', { rows: rowCount, format: 'xlsx' });

Имя события — латиница, нижний регистр, прошедшее время; различия между случаями идут в данные, а не в имя. Правила целиком — Словарь событий.

Поля, которые не меняются от события к событию, ставьте контекстом там, где факт стал известен, — тогда их не придётся повторять в каждом вызове:

setContext({ user_id: user.id, plan: user.plan });   // после входа
setContext({ workspace_id: workspaceId });           // при переключении рабочей области

Слои живут в памяти страницы и после перезагрузки проставляются заново.

Время на экране меряйте сами

Счётчик отправляет время в событии закрытия страницы, но для вопроса «сколько минут смотрели вот этот экран» оно не годится: в том событии нет адреса, а время активности копится по странице целиком. В обычном сайте это одно и то же, в SPA — нет.

Поэтому экран, время на котором нужно померить, меряет себя сам:

let openedKey = '', openedAt = 0;

function openScreen(code: string) {
  if (code === openedKey) return;        // подписки роутера срабатывают чаще, чем меняется экран
  closeScreen();
  openedKey = code; openedAt = Date.now();
  track('screen_opened', { screen: code });
}

function closeScreen() {
  if (!openedKey) return;
  track('screen_closed', { screen: openedKey, duration_ms: Date.now() - openedAt });
  openedKey = '';
}

Накройте три случая, иначе половина «закрытий» не отправится: смена экрана внутри приложения, сворачивание вкладки (visibilitychange — если считаете активное время отдельно) и уход со страницы (pagehide; beforeunload на мобильных не срабатывает).

Проверка

Откройте живое приложение на боевом домене, откройте вкладку Network и сделайте действие, на которое повесили вызов. Признак успеха — запрос visit.php?type=event; в его теле имя вашего события и поля внутри data.

Если запроса нет, добавьте к адресу ?ko_debug и повторите: счётчик начнёт писать в консоль, что с ним происходит.

Числа в отчёте появятся после того, как вы соберёте из события метрику, — это следующий раздел. Но факт отправки виден здесь и сразу, без ожидания.

Соберите из события метрику

  1. В разделе Метрики проекта создайте метрику: свой ключ (например appReportExported) и формула {sites_events:visitor:report_exported} — имя вашего события в третьей части.
  2. Долю и конверсию считайте формулой по ключам метрик: {appExports}/{appReports}.
  3. Уникальные люди, а не нажатия: {sites_events:visitor:report_exported[:value=visitors_id][:aggregate=uniq]}.
  4. Разрез по данным события — измерением field:data.product в секции Разрез вашего отчёта. Отдельная метрика на каждое значение не нужна.
  5. Если событие несёт деньги, передавайте сумму полем income рядом с data, а не внутри него, — тогда она складывается как деньги.

Если не сходится

Метрика показывает 0, хотя события идут. Почти всегда двоеточие в имени события. Проверьте имя: только латиница, цифры и _.

В разрезе одна пустая строка. Данные события передали плоским объектом. Рабочая форма — { data: { … } }, её обеспечивает обёртка; прямой вызов счётчика мимо обёртки её теряет.

События вообще не появляются. Счётчик не загрузился (проверьте в чистом профиле, без блокировщика) или вызов случился на домене, который вы сами и отключили гейтом.

Событий вдвое больше, чем действий. Вызов стоит на отрисовке, а не на факте: экран перерисовался — событие ушло снова.

Поле в отчёте не то, что передали. Ключ есть и в контексте, и в данных события — побеждает контекст. Один ключ живёт где-то одном.

Числа выглядят слишком хорошо. Проверьте, не попадает ли в проект ваша собственная разработка: в отчёте это видно разрезом по домену.

Что рядом