Livewire
Blade-директивы @seo не зависят от фронтенд-фреймворка: они выводят обычный HTML в <head>, поэтому в любом приложении Livewire работают так же, как в Blade.
Первоначальный вывод полной страницы
В полностраничном компоненте Livewire (когда маршрут возвращает компонент) или в любом Blade-шаблоне, содержащем компоненты Livewire, @seo работает точно так же, как в руководстве по Blade:
{{-- layouts/app.blade.php --}}
<head>
@seo($post ?? null)
</head>Первый HTTP-ответ содержит полный head, доступный роботам: заголовок, описание, каноническую ссылку, Open Graph, Twitter и JSON-LD. Именно этот ответ получают поисковые роботы и сборщики превью соцсетей, и в нём всё выводится корректно.
Особенность wire:navigate
wire:navigate в Livewire превращает переходы по ссылкам в навигацию в стиле SPA. При таком переходе Livewire заменяет <body> и объединяет <head>, но для SEO-пакета важно одно различие:
<title>и<meta>/<link>объединяются с head новой страницы, поэтому итоговые заголовок и метаданные обычно обновляются.<script>считается неудаляемым ресурсом. Livewire сохраняет каждый встреченный<script>, чтобы повторное выполнение не нарушило работу JavaScript. Поэтому блоки JSON-LD<script>накапливаются: после посещения трёх записей в head одновременно остаётся разметка всех трёх, а инструмент, читающий структурированные данные, видит неверные или несколько сущностей.
Чтобы их можно было очищать, средство вывода помечает каждый создаваемый JSON-LD script:
<script type="application/ld+json" data-seo-schema
data-seo-url="https://example.com/blog/the-post"> … </script>Добавьте очистку JSON-LD
Добавьте этот код один раз (например, в корневом шаблоне после @livewireScripts). При каждом wire:navigate он оставляет разметку только текущей страницы и удаляет устаревшую:
<script>
document.addEventListener('livewire:navigated', () => {
// The page we are now on. data-seo-url is the canonical (query-stripped),
// so compare on the query-stripped location.
const here = window.location.href.split('#')[0].split('?')[0]
// Keep only the LAST schema for this page; remove every other-URL
// (stale) script AND same-URL duplicates Livewire re-adds when a page is
// revisited — including clearing a lone stale script when this page has
// none. Iterate from the end so the freshest copy is the one kept.
const scripts = [...document.querySelectorAll('script[data-seo-schema]')]
let kept = false
for (let i = scripts.length - 1; i >= 0; i--) {
const url = (scripts[i].getAttribute('data-seo-url') || '').split('?')[0]
if (url === here && !kept) { kept = true; continue }
scripts[i].remove()
}
})
</script>Используются только маркер data-seo-schema и идентификатор для каждого URL, которые средство вывода уже добавляет. Настраивать каждую страницу отдельно не требуется.
Сравнивайте с текущим URL, а не с последним добавленным script
Предыдущая версия этого примера завершалась, если скриптов schema было меньше двух, и считала последний добавленный скрипт текущей страницей. Из-за этого при переходе со страницы с JSON-LD на страницу без него в head оставалась устаревшая разметка (присутствовал только старый скрипт, который сохранялся из-за раннего возврата). Такой код также не удалял дубликат для того же URL, который Livewire добавляет при повторном посещении страницы. Сравнение каждого data-seo-url с window.location и сохранение только последнего совпадения во всех случаях удаляет и устаревшую разметку, и дубликаты. Именно это проверяют приложение Livewire rankbeam-examples и его браузерный тест.
Одиночные метатеги при SPA-навигации
Объединение head в Livewire в большинстве случаев не даёт одиночным тегам <meta>/<link> устареть, но точное поведение зависит от версии Livewire и структуры шаблона. Для страниц, где корректные метаданные для роботов критичны, предпочитайте полную перезагрузку страницы (обычную ссылку без wire:navigate) или серверный рендеринг, чтобы первый HTTP-ответ был определяющим. Приложение Livewire rankbeam-examples проверяет реальный сценарий wire:navigate в браузере.
Filament
В основе Filament лежит Livewire, но это интерфейс администратора для редактирования: он изменяет seo_meta и никогда не выводит head публичного фронтенда. См. руководство по Filament; описанные здесь особенности к панели администратора не относятся.