Data publikacji: 28 sierpnia 2025 r.
Wyszukiwarka Google ma jeden z największych zasięgów na świecie, dlatego zmiany w interfejsie mogą mieć wpływ na miliardy użytkowników. Od dawna marzyliśmy o internecie, który byłby bardziej nowoczesny i przypominał aplikację. Gdy rozpoczęliśmy prace nad trybem AI, chcieliśmy stworzyć dla użytkowników środowisko, w którym przejście z wyszukiwania standardowego do trybu AI będzie płynne i spójne. Gdy dowiedzieliśmy się o przejściach między dokumentami, wiedzieliśmy, że to idealne rozwiązanie dla tej funkcji. W tym studium przypadku opisujemy, czego dowiedzieliśmy się podczas dodawania funkcji przejścia wraz z wprowadzeniem Trybu AI.
Przejścia między dokumentami to rewolucja w zakresie natywnych narzędzi przeglądarki. Z niecierpliwością czekamy na to, jak wpłyną one na przyszłość internetu.
Zmiana status quo
Wyszukiwarka Google ma rygorystyczne i konserwatywne wymagania dotyczące obsługi przeglądarek. Zwykle korzystanie z funkcji o ograniczonej dostępności było niedozwolone. W przypadku przejść między dokumentami okazało się, że polyfill nie jest dobrym rozwiązaniem. Główną przeszkodą był brak interfejsu API do tworzenia migawek pikseli, a klonowanie całego obszaru widoku powodowało poważne problemy z wydajnością. Dlatego najlepszym sposobem na wprowadzenie tej funkcji wraz z Trybem AI było zastosowanie progresywnego ulepszania. Animacje tworzone przez przejścia widoku nie mają bezpośredniego wpływu na funkcjonalność witryny, więc w przypadku nieobsługiwanego ruchu po prostu zostaną wyłączone. Był to już obecny stan produkcyjny bez animacji przejść.
Najpierw przetestowaliśmy tę strategię progresywnego ulepszania na naszych użytkownikach wewnętrznych. Dzięki temu uzyskaliśmy wstępne opinie, które były w większości pozytywne. Otrzymane opinie ujawniły też błędy, w tym problemy z wydajnością i niezamierzone interakcje z innymi funkcjami, np. nakładające się konteksty układania.
Ta strategia okazała się skuteczna i wierzę, że w przyszłości będziemy ją stosować w przypadku innych nowych funkcji przeglądarki.
trudności, które napotkaliśmy, i sposoby ich rozwiązania;
Opóźnienie, blokowanie renderowania i zegary nadzorcze
Ogólnie rzecz biorąc, dodatkowe opóźnienie związane z przejściami widoku MPA jest pomijalne w 99% przypadków użycia, zwłaszcza na nowoczesnym sprzęcie. Wyszukiwarka Google ma jednak bardzo wysokie wymagania, jeśli chodzi o opóźnienia, i staramy się tworzyć rozwiązania, które działają dobrze na wszystkich urządzeniach. Dla nas liczą się nawet dodatkowe milisekundy, dlatego musieliśmy zainwestować w prawidłowe wdrożenie przejść między widokami w różnych dokumentach bez pogarszania wrażeń użytkowników.
Blokowanie renderowania to technika, która dobrze sprawdza się w przypadku przejść między widokami w różnych dokumentach. Zrzuty pseudoelementów w przychodzącym dokumencie mogą wyświetlać tylko treści, które zostały już wyrenderowane. Dlatego, aby animować treści z dokumentu przychodzącego, musisz renderować blok, dopóki nie zostanie wyrenderowany element docelowy, który chcesz animować. Aby to zrobić, użyj atrybutu blocking w przypadku elementu HTMLLinkElement. Blokowanie renderowania ma swoje wady, ponieważ oczekiwanie na element znajdujący się pod koniec drzewa DOM przychodzącego dokumentu może mieć znaczny wpływ na opóźnienie. Musieliśmy odpowiednio zrównoważyć tę zależność i renderować blokowanie tylko w przypadku elementów, które są renderowane bardzo wcześnie w cyklu życia strony.
<!-- Link tag in the <head> of the incoming document -->
<link blocking="render" href="#target-id" rel="expect">
<!-- Element you want to animate in the <body> of the incoming document -->
<div id="target-id">
some content
</div>
W niektórych przypadkach precyzyjne określenie elementu, w którym ma być renderowany blok, nie wystarczało. Niektóre urządzenia lub połączenia nadal będą wykazywać dodatkowe opóźnienie, nawet jeśli blokowanie renderowania będzie dotyczyć elementu znajdującego się na początku drzewa DOM. W takich przypadkach napisaliśmy skrypt zegara nadzorującego, który po upływie określonego czasu usuwa znak HTMLLinkElement, aby wymusić odblokowanie renderowania przychodzącego dokumentu.
Możesz to zrobić w prosty sposób:
function unblockRendering() {
const renderBlockingElements = document.querySelectorAll(
'link[blocking=render]',
);
for (const element of renderBlockingElements) {
element.remove();
}
}
const timeToUnblockRendering = t - performance.now();
if (timeToUnblockRendering > 0) {
setTimeout(unblockRendering, timeToUnblockRendering);
} else {
unblockRendering();
}
Ograniczenia ochrony
Inny problem, który napotkaliśmy, polega na tym, że reguła @ navigation: auto przejść widoku między dokumentami występuje na poziomie globalnym w dokumencie. Nie ma wbudowanego sposobu na ograniczenie włączania przejść między widokami w różnych dokumentach tylko do określonych celów kliknięć. Ze względu na to, że jest to duża zmiana, nie mogliśmy włączyć przejść między dokumentami w przypadku wszystkich nawigacji w wyszukiwarce Google. Potrzebowaliśmy sposobu na dynamiczne włączanie i wyłączanie przejść między widokami w różnych dokumentach w zależności od tego, z jaką funkcją użytkownik wchodził w interakcję. W naszym przypadku włączyliśmy je tylko w przypadku zmian trybu na tryb AI i z niego. Zrobiliśmy to, programowo aktualizując regułę @navigation w zależności od tego, który element docelowy został kliknięty lub dotknięty.
Sposób przełączania reguły @view-transition jest następujący:
let viewTransitionAtRule: HTMLElement | undefined;
const DISABLED_VIEW_TRANSITION = '@view-transition{navigation:none;}';
const ENABLED_VIEW_TRANSITION = '@view-transition{navigation:auto;}';
function getVtAtRule(): HTMLElement {
if (!viewTransitionAtRule) {
viewTransitionAtRule = document.createElement('style');
document.head.append(viewTransitionAtRule);
}
return viewTransitionAtRule;
}
function disableVt() {
getVtAtRule().textContent = DISABLED_VIEW_TRANSITION;
}
function enableVt() {
getVtAtRule().textContent = ENABLED_VIEW_TRANSITION;
}
Zacięcia i animacje złożone
Niektóre automatycznie generowane animacje w pseudo-elementach przejścia widoku powodowały spadek liczby klatek na starszych urządzeniach, co pogarszało płynność i wygodę korzystania z witryny. Aby poprawić wydajność animacji, przepisaliśmy je, używając technik animacji, które mogą działać w kompozytorze. Udało nam się to zrobić, sprawdzając klatki kluczowe, aby uzyskać wymiary pseudoelementów przed i po zrzucie ekranu, oraz używając matematyki macierzowej do odpowiedniego przepisania klatek kluczowych. W przykładzie poniżej pokazujemy, jak pobrać animację dla każdego pseudoelementu przejścia widoku:
const pseudoElement = `::view-transition-group(${name})`;
const animation = document
.getAnimations()
.find(
(animation) =>
(animation.effect as KeyframeEffect)?.pseudoElement === pseudoElement,
);
Więcej informacji o tworzeniu wydajnych klatek kluczowych przejścia widoku znajdziesz w artykule View Transitions Applied: Dealing with the Snapshot Containing Block (Przejścia widoku: radzenie sobie z blokiem zawierającym migawkę).
Inne kwestie, na które warto zwrócić uwagę
Jednym z najważniejszych problemów jest to, że oznaczanie elementów za pomocą właściwości CSS view-transition-name wpływa na kontekst układania (specyfikacja przejść widoku: sekcja 2.1.1). Było to źródłem wielu błędów, które wymagały modyfikacji z-index elementów kontenera.
Pamiętaj też, że domyślnie nie warto dodawać do elementów wartości view-transition-name. Nad wyszukiwarką Google pracuje wiele osób. Aby zapobiec konfliktom między wartościami view-transition-name ustawianymi przez nasz zespół na elementach a wartościami, których mogą używać osoby z innych zespołów, zastosowaliśmy typy przejść widoku, aby warunkowo dodawać właściwość view-transition-name tylko wtedy, gdy aktywny jest określony typ przejścia widoku.
Przykładowy kod CSS, który dodaje view-transition-name o wartości the-element do elementu tylko wtedy, gdy aktywny jest typ przejścia widoku ai-mode:
html:active-view-transition-type(ai-mode) {
#target {
view-transition-name: the-element;
}
}
Gdy masz już te reguły CSS dla wszystkich przejść widoku, możesz dynamicznie zmieniać bieżący typ przejścia widoku w przypadku dowolnej nawigacji podczas zdarzeń pageswap i pagereveal.
Przykład aktualizacji typu przejścia widoku na ai-mode podczas zdarzenia pageswap.
function updateViewTransitionTypes(
event: ViewTransitionEvent,
types: string[],
): void {
event.viewTransition.types.clear();
for (const type of types) {
event.viewTransition.types.add(type);
}
}
window.addEventListener(
'pageswap',
(e) => {
updateViewTransitionTypes(
e as ViewTransitionEvent,
['ai-mode'],
);
}
);
W ten sposób zapobiegamy kolizjom nazw i niepotrzebnie nie robimy zrzutów elementów, które nie muszą być zrzutami w ramach przechodzenia do trybu AI i z niego.
Na koniec dodajmy, że wszelkie problemy z kontekstem układania w stos będą występować tylko podczas przejścia widoku. Aby rozwiązać te problemy, możemy kierować reklamy na indeksy z wygenerowanych pseudoelementów zamiast dowolnie modyfikować indeksy z oryginalnych elementów tylko po to, aby rozwiązać ten problem podczas korzystania z przejść widoku.
::view-transition-group(the-element) {
z-index: 100;
}
Co dalej?
Planujemy używać przejść między widokami w różnych dokumentach w wyszukiwarce Google, w tym integracji z interfejsem Navigation API, gdy tylko stanie się on dostępny w różnych przeglądarkach. Śledź nasze postępy, aby zobaczyć, co stworzymy w dalszej kolejności.