AstroMiddleTechnical

Что такое View Transitions в Astro и как их включить?

View Transitions — API браузера для анимированных переходов между страницами без полной перезагрузки. В Astro включаются добавлением компонента <ViewTransitions /> в <head> layout и директив transition:* на элементах.

Что такое View Transitions

View Transitions — стандартный браузерный API (Chrome 111+, поддержан через полифил в остальных), позволяющий анимировать переход между двумя состояниями DOM без полной перезагрузки страницы. В Astro это реализовано поверх стандартного API с добавлением маршрутизации на стороне клиента: при клике по ссылке Astro перехватывает навигацию, загружает новую страницу через fetch и применяет CSS-анимацию перехода между старым и новым DOM-деревом.

Включение View Transitions

Достаточно импортировать компонент ViewTransitions и поместить его в <head> базового layout:

---
// src/layouts/BaseLayout.astro
import { ViewTransitions } from 'astro:transitions';
---
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <title>Мой сайт</title>
    <ViewTransitions />
  </head>
  <body>
    <slot />
  </body>
</html>

После этого все навигации между страницами, использующими этот layout, становятся анимированными. По умолчанию применяется анимация fade.

Именованные переходы с transition:name

Директива transition:name позволяет браузеру плавно «перелетать» конкретный элемент с одной страницы на другую — это называется shared element transition:

---
// src/pages/index.astro
const posts = await getCollection('blog');
---
{posts.map(post => (
  <a href={`/blog/${post.slug}`}>
    <img
      src={post.data.cover}
      alt={post.data.title}
      transition:name={`cover-${post.slug}`}
    />
    <h2 transition:name={`title-${post.slug}`}>{post.data.title}</h2>
  </a>
))}
---
// src/pages/blog/[slug].astro
const { post } = Astro.props;
---
<img
  src={post.data.cover}
  alt={post.data.title}
  transition:name={`cover-${post.slug}`}
/>
<h1 transition:name={`title-${post.slug}`}>{post.data.title}</h1>

Управление анимацией: transition:animate

Директива transition:animate задаёт тип анимации для элемента или всей страницы:

import { slide, fade, none } from 'astro:transitions';

<!-- Весь main скользит при переходе -->
<main transition:animate={slide()}>>
  <slot />
</main>

<!-- Навигация не анимируется -->
<nav transition:animate={none()}>>
  ...
</nav>

<!-- Кастомная анимация -->
<section transition:animate={{
  forwards: {
    old: [{ opacity: 1 }, { opacity: 0 }],
    new: [{ opacity: 0 }, { opacity: 1 }],
    easing: 'ease-in-out',
    duration: '0.3s'
  },
  backwards: {
    old: [{ opacity: 1 }, { opacity: 0 }],
    new: [{ opacity: 0 }, { opacity: 1 }]
  }
}}>

События жизненного цикла

Astro генерирует события на document, которые можно слушать для выполнения JS-логики при переходах:

// src/scripts/analytics.js
document.addEventListener('astro:page-load', () => {
  // Вызывается после каждого перехода и при первой загрузке
  console.log('Страница загружена:', window.location.pathname);
  initAnalytics();
});

document.addEventListener('astro:before-preparation', (event) => {
  // event.from — текущий URL, event.to — целевой URL
  // Можно отменить переход: event.preventDefault()
});

document.addEventListener('astro:after-swap', () => {
  // Новый DOM уже вставлен, но анимация ещё идёт
  restoreFocusState();
});

Директива transition:persist

Элементы с transition:persist сохраняются между переходами без пересоздания — полезно для аудиоплееров, видео, состояния форм:

<video src="/podcast.mp3" transition:persist controls />

Подводные камни

  • View Transitions поддерживаются нативно только в Chromium-браузерах; Firefox и Safari используют полифил Astro, который работает через fade без CSS-анимаций браузера — визуальный результат отличается.
  • transition:name должен быть уникальным на каждой странице в каждый момент времени — дублирующиеся имена приводят к непредсказуемой анимации или её отсутствию.
  • Скрипты, вставленные через обычные <script>-теги, не перевыполняются при переходе — нужно слушать событие astro:page-load для инициализации JS после навигации.
  • Компоненты React/Vue/Svelte теряют своё состояние при переходе, если не используется transition:persist — это нарушает ожидания при работе с управляемыми формами или другим интерактивным состоянием.
  • Анимации из CSS @keyframes, заданные через transition:animate на элементах с transition:name, могут конфликтовать с браузерным анимированием shared-элементов.
  • Хэш-навигация (#anchor) по умолчанию не запускает View Transition — нужно явно обрабатывать этот случай.
  • При использовании transition:persist на компонентах с серверными пропсами — данные не обновляются при переходе, что приводит к устаревшему содержимому.
  • Если layout-компонент не является общим для обеих страниц, переход между ними не будет иметь анимации — стоит убедиться, что оба маршрута используют одинаковый layout с ViewTransitions.

Common mistakes

  • Полагаться, что View Transitions API доступен везде — нужен fallback.
  • Забывать переинициализировать сторонние библиотеки после astro:after-swap.
  • Использовать одинаковые transition:name для не связанных элементов.

What the interviewer is testing

  • Знает компонент <ClientRouter /> и его роль.
  • Понимает атрибуты transition:name, transition:persist.
  • Помнит про события lifecycle перехода.

Sources

Related topics