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

Этот сайт сделан на Blazor, и Skomi измеряет его именно так.

Сначала возьмите сниппет в своей панели:

<script src="https://t.skomi.com/s/YOUR-SITE-ID.js" defer></script>

Куда он идёт

Ваш проект Файл Место в нём
Blazor Web App (.NET 8+) Components/App.razor В <body>, после <Routes />
Blazor WebAssembly, отдельный wwwroot/index.html В <body>, перед скриптом фреймворка
Blazor Server (.NET 6/7) Pages/_Host.cshtml или Pages/_Layout.cshtml В <body>
MAUI Blazor Hybrid не применимо Не сюда: это приложение, смотрите установку на MAUI

Для Blazor Web App это выглядит так:

<body>
    <Routes />

    <script src="https://t.skomi.com/s/YOUR-SITE-ID.js" defer></script>
    <script src="_framework/blazor.web.js"></script>
</body>

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

Скрипт, вставленный расширенной навигацией, не выполняется. Blazor патчит DOM, а не перезагружает документ, и тег <script>, пришедший в составе этого патча, не запускается. Поэтому сниппет, положенный в MainLayout.razor или в компонент страницы, работает при первой загрузке, тихо мёртв после первого внутреннего перехода и всё это время выглядит установленным.

App.razor находится вне всего, что Blazor заменяет. Тег загружается один раз и остаётся загруженным.

Переходы считаются за вас

Происходят две разные вещи в зависимости от того, везёте ли вы среду выполнения Blazor, и обе уже обработаны.

С blazor.web.js внутренние ссылки используют расширенную навигацию: документ выживает, а адрес меняется через history.pushState. Скрипт Skomi оборачивает pushState и replaceState и слушает popstate, так что каждое из этого — просмотр страницы. Подписываться не на что, вызывать при переходе нечего.

Без неё — приложение Blazor, отрисованное статически и без интерактивности, не везёт среду выполнения вовсе, поэтому каждая ссылка это обычная загрузка страницы и считается как на любом другом сайте. Этот сайт — как раз такой случай.

Знать, какой у вас случай, не обязательно: тот же тег в том же месте верен для обоих.

Переход, не меняющий адрес, не считается, ни в одном из режимов. Вкладки, раскрывающиеся блоки и смена фильтров без параметров в адресе — это взаимодействия, а не просмотры страниц. Если вы хотите их измерять, шлите событие.

Интерактивные режимы отрисовки здесь ничего не меняют

InteractiveServer, InteractiveWebAssembly и InteractiveAuto решают, где выполняются ваши компоненты, а не как страница доставляется в браузер. Сниппет стоит выше всего этого в App.razor и ведёт себя одинаково при каждом. Режимы отрисовки на уровне компонентов тоже не имеют значения: документ один, и скрипт один.

Если ничего не приходит

  • Тег в App.razor, а не в макете? Смотрите предупреждение выше; ради этой ошибки эта страница и существует.
  • Он внутри <body>? В <head> он тоже работает, но не путайтесь под ногами у <HeadOutlet />: этот вывод предназначен для содержимого, которое дают компоненты, а трекер таковым не является.
  • dotnet watch и закешированный документ. Расширенная навигация и горячая перезагрузка могут оставить вас смотрящим на страницу, собранную до добавления тега. Обновите один раз с очисткой кеша.
  • Localhost не считается. Выложите или проверяйте на настоящем имени хоста.

Что делает сниппет после загрузки, смотрите в установке сниппета, а измерение приложения, если измеряете вы программу, которую люди устанавливают, а не сайт, на который они заходят.