1. Hinweis
In diesem Codelab erfahren Sie, wie Sie einer Beispiel-Web-App mit den neuesten APIs, die Google Chrome nativ unterstützt, eine sofortige Navigation und nahtlose Seitenübergänge hinzufügen.
Die Beispiel-Web-App prüft die Nährwerte beliebter Obst- und Gemüsesorten. Die Seiten „fruit-list“ und „fruit-details“ sind als Single-Page-Anwendung (SPA) aufgebaut, die Seiten „vegetable-list“ und „vegetable-details“ als traditionelle Multi-Page-Anwendung (MPA).

Konkret implementieren Sie Prerendering, Back-Forward-Cache (bfcache) und Private Prefetch Proxy für die sofortige Navigation sowie Übergänge von Root- und gemeinsam genutzten Elementen für die nahtlosen Seitenübergänge. Sie implementieren das Vorrendern und den BFCache für die MPA-Seiten und Übergänge für gemeinsame Elemente für die SPA-Seiten.
Die Websitegeschwindigkeit ist immer ein wichtiger Aspekt der Nutzerfreundlichkeit. Aus diesem Grund hat Google die Core Web Vitals eingeführt. Diese Messwerte geben Aufschluss über die Ladeleistung, Interaktivität und visuelle Stabilität von Webseiten und damit über die Nutzerfreundlichkeit im praktischen Einsatz. Mit den neuesten APIs können Sie die Core Web Vitals-Werte Ihrer Website in der Praxis verbessern, insbesondere die Ladeleistung.

Demo von Mindvalley
Nutzer sind es auch gewohnt, dass Übergänge in nativen mobilen Apps verwendet werden, um die Navigation und Zustandsänderungen besonders intuitiv zu gestalten. Leider ist die Nachbildung solcher Nutzererlebnisse im Web nicht einfach. Mit den aktuellen Webplattform-APIs lassen sich zwar ähnliche Effekte erzielen, die Entwicklung kann jedoch zu schwierig oder komplex sein, insbesondere im Vergleich zu den entsprechenden Funktionen in Android- oder iOS-Apps. Seamless APIs sollen diese Lücke zwischen App und Web für Nutzer und Entwickler schließen.

Vorbereitung
Kenntnisse in folgenden Bereichen:
- HTML
- CSS
- JavaScript
- Google Chrome-Entwicklertools
Die Themen:
Implementierung:
- Pre-Rendering
- bfcache
- Private Prefetch Proxy
- Übergänge für Root- und freigegebene Elemente
Aufgaben
Eine Beispiel-Web-App, die mit Next.js erstellt wurde und die neuesten sofortigen und nahtlosen Browserfunktionen nutzt:
- Nahezu sofortige Navigation durch Vorrendern
- bfcache für sofortiges Laden mit den Browser-Schaltflächen „Zurück“ und „Vorwärts“
- Guter erster Eindruck durch ursprungsübergreifende Navigation mit Private Prefetch Proxy oder Signed Exchange (SXG)
- Nahtloser Übergang zwischen Seiten mit Übergang von Root-/gemeinsamen Elementen
Voraussetzungen
- Chrome ab Version 101
2. Jetzt starten
Chrome-Flags aktivieren
- Rufen Sie „about://flags“ auf und aktivieren Sie die Laufzeit-Flags
Prerender2unddocumentTransition API. - Starten Sie den Browser neu.
Code abrufen
- Öffnen Sie den Code aus diesem GitHub-Repository in Ihrer bevorzugten Entwicklungsumgebung:
git clone -b codelab git@github.com:googlechromelabs/instant-seamless-demo.git
- Installieren Sie die Abhängigkeiten, die zum Ausführen des Servers erforderlich sind:
npm install
- Starten Sie den Server auf Port 3000:
npm run dev
- Rufen Sie in Ihrem Browser http://localhost:3000 auf.
Jetzt können Sie Ihre App bearbeiten und verbessern. Wenn Sie Änderungen vornehmen, wird die App neu geladen und Ihre Änderungen sind direkt sichtbar.
3. Vorrendern einbinden
Für diese Demo ist die Ladezeit der Seite mit den Gemüsedetails in der Beispiel-App aufgrund einer willkürlichen Verzögerung auf dem Server sehr langsam. Durch das Vorrendern wird diese Wartezeit vermieden.
So fügen Sie der Seite „Gemüseliste“ Prerender-Schaltflächen hinzu, die das Prerendering nach einem Nutzerklick auslösen:
- Erstellen Sie eine Schaltflächenkomponente, die das Spekulationsregeln-Script-Tag dynamisch einfügt:
components/prerender-button.js
import { useContext } from 'react'
import ResourceContext from './resource-context'
// You use resource context to manage global states.
// In the PrerenderButton component, you update the prerenderURL parameter when the button is clicked.
export default function PrerenderButton() {
const { dispatch } = useContext(ResourceContext)
const handleClick = (e) => {
e.preventDefault()
e.stopPropagation()
const parent = e.target.closest('a')
if (!parent) {
return
}
const href = parent.getAttribute('href')
dispatch({ type: 'update', prerenderURL: href })
}
return (
<button className='ml-auto bg-gray-200 hover:bg-gray-300 px-4 rounded' onClick={handleClick}>
Prerender
</button>
)
}
- Importieren Sie die Komponente
PrerenderButtonin die Dateilist-item.js.
components/list-item.js
// Codelab: Add a PrerenderButton component.
import PrerenderButton from './prerender-button'
...
function ListItemForMPA({ item, href }) {
return (
<a href={href} className='block flex items-center'>
<Icon src={item.image} />
<div className='text-xl'>{item.name}</div>
{/* Codelab: Add PrerenderButton component. */}
<PrerenderButton />
</a>
)
}
- Komponente zum Hinzufügen der Speculation Rules API erstellen
Die Komponente SpeculationRules fügt dynamisch ein Script-Tag in die Seite ein, wenn die App den Status prerenderURL aktualisiert.
components/speculationrules.js
import Script from 'next/script'
import { useContext, useMemo } from 'react'
import ResourceContext from './resource-context'
export default function SpeculationRules() {
const { state } = useContext(ResourceContext)
const { prerenderURL } = state
return useMemo(() => {
return (
<>
{prerenderURL && (
<Script id='speculationrules' type='speculationrules'>
{`
{
"prerender":[
{
"source": "list",
"urls": ["${prerenderURL}"]
}
]
}
`}
</Script>
)}
</>
)
}, [prerenderURL])
}
- Binden Sie die Komponenten in die App ein.
pages/_app.js
// Codelab: Add the SpeculationRules component.
import SpeculationRules from '../components/speculationrules'
function MyApp({ Component, pageProps }) {
useAnalyticsForSPA()
return (
<ResourceContextProvider>
<Layout>
<Component {...pageProps} />
</Layout>
{/* Codelab: Add SpeculationRules component */}
<SpeculationRules />
<Script id='analytics-for-mpa' strategy='beforeInteractive' src='/analytics.js' />
</ResourceContextProvider>
)
}
export default MyApp
- Klicken Sie auf Vorrendern.
Wie Sie sehen, hat sich die Ladezeit deutlich verbessert. Im tatsächlichen Anwendungsfall wird das Vorrendern für die Seite ausgelöst, die der Nutzer wahrscheinlich als Nächstes besuchen wird.

Analytics
Standardmäßig wird im analytics.js-Dokument der Beispiel-Web-App ein Seitenaufruf-Ereignis gesendet, wenn das DOMContentLoaded-Ereignis eintritt. Das ist jedoch nicht ratsam, da dieses Ereignis während der Vorrenderungsphase ausgelöst wird.
So führen Sie ein document.prerendering- und prerenderingchange-Ereignis ein, um dieses Problem zu beheben:
- Schreiben Sie die Datei
analytics.jsneu:
public/analytics.js
const sendEvent = (type = 'pageview') => {
// Codelab: Make analytics prerendering compatible.
// The pageshow event could happen in the prerendered page before activation.
// The prerendered page should be handled by the prerenderingchange event.
if (document.prerendering) {
return
}
console.log(`Send ${type} event for MPA navigation.`)
fetch(`/api/analytics?from=${encodeURIComponent(location.pathname)}&type=${type}`)
}
...
// Codelab: Make analytics prerendering compatible.
// The prerenderingchange event is triggered when the page is activated.
document.addEventListener('prerenderingchange', () => {
console.log('The prerendered page was activated.')
sendEvent()
})
...
Super, du hast deine Analysen erfolgreich so angepasst, dass sie mit dem Prerendering kompatibel sind. Die Seitenaufruf-Logs werden jetzt mit dem richtigen Timing in der Browserkonsole angezeigt.
4. bfcache-Blockierungen entfernen
unload-Event-Handler entfernen
Ein unnötiges unload-Ereignis ist ein sehr häufiger Fehler, der nicht mehr empfohlen wird. Dadurch wird nicht nur verhindert, dass bfcache funktioniert, sondern es ist auch unzuverlässig. Das Tag wird beispielsweise nicht immer auf Mobilgeräten und in Safari ausgelöst.
Statt des unload-Ereignisses verwenden Sie das pagehide-Ereignis, das in allen Fällen ausgelöst wird, in denen das unload-Ereignis ausgelöst wird und wenn eine Seite im bfcache gespeichert wird.
So entfernen Sie den unload-Ereignis-Handler:
- Ersetzen Sie in der Datei
analytics.jsden Code für denunload-Ereignishandler durch den Code für denpagehide-Ereignishandler:
public/analytics.js
// Codelab: Remove the unload event handler for bfcache.
// The unload event handler prevents the content from being stored in bfcache. Use the pagehide event instead.
window.addEventListener('pagehide', () => {
sendEvent('leave')
})
Cachesteuerungsheader aktualisieren
Seiten, die mit einem Cache-control: no-store-HTTP-Header ausgeliefert werden, profitieren nicht von der bfcache-Funktion des Browsers. Daher ist es ratsam, diesen Header sparsam zu verwenden. Wenn die Seite keine personalisierten oder kritischen Informationen wie den Anmeldestatus enthält, müssen Sie sie wahrscheinlich nicht mit dem Cache-control: no-store-HTTP-Header bereitstellen.
So aktualisieren Sie den Cache-Control-Header der Beispiel-App:
- Ändern Sie den
getServerSideProps-Code:
pages/vegetables/index.js
export const getServerSideProps = middleware(async (ctx) => {
const { req, res } = ctx
// Codelab: Modify the cache-control header.
res.setHeader('Cache-Control', 'public, s-maxage=10, stale-while-revalidate=59')
...
pages/vegetables/[name].js
export const getServerSideProps = middleware(async (ctx) => {
const { req, res, query } = ctx
// Codelab: Modify the cache-control header.
res.setHeader('Cache-Control', 'public, s-maxage=10, stale-while-revalidate=59')
...
Feststellen, ob eine Seite aus dem BFCache wiederhergestellt wurde
Das pageshow-Ereignis wird direkt nach dem load-Ereignis ausgelöst, wenn die Seite zum ersten Mal geladen wird und jedes Mal, wenn die Seite aus dem Back-Forward-Cache wiederhergestellt wird. Das pageshow-Ereignis hat die Property persisted, die „true“ ist, wenn die Seite aus dem bfcache wiederhergestellt wurde, und „false“, wenn dies nicht der Fall war. Mit der Eigenschaft persisted können Sie normale Seitenaufrufe von bfcache-Wiederherstellungen unterscheiden. Die meisten wichtigen Analysedienste sollten bfcache berücksichtigen. Sie können aber prüfen, ob die Seite aus dem bfcache wiederhergestellt wird, und Ereignisse manuell senden.
So prüfen Sie, ob eine Seite aus dem bfcache wiederhergestellt wurde:
- Fügen Sie diesen Code in die Datei
analytics.jsein.
public/analytics.js
// Codelab: Use the pageshow event handler for bfcache.
window.addEventListener('pageshow', (e) => {
// If the persisted flag exists, the page was restored from bfcache.
if (e.persisted) {
console.log('The page was restored from bfcache.')
sendEvent()
}
})
Webseite debuggen
Mit den Chrome-Entwicklertools können Sie Ihre Seiten testen, um sicherzustellen, dass sie für den bfcache optimiert sind, und alle Probleme ermitteln, die dazu führen könnten, dass sie nicht infrage kommen.
So testen Sie eine bestimmte Seite:
- Rufen Sie die Seite in Chrome auf.
- Klicken Sie in den Chrome-Entwicklertools auf Anwendung > Back-Forward-Cache > Test ausführen.
Die Chrome-Entwicklertools versuchen, die Seite zu verlassen und dann wieder aufzurufen, um festzustellen, ob sie aus dem Back-Forward-Cache wiederhergestellt werden kann.

Wenn die Seite erfolgreich aus dem Back-Forward-Cache wiederhergestellt wurde, wird im Bereich Folgendes angezeigt:

Wenn die Wiederherstellung nicht möglich ist, wird im Bereich angezeigt, dass die Seite nicht wiederhergestellt wurde, und der Grund dafür. Wenn der Grund etwas ist, das Sie als Entwickler beheben können, wird das im Bereich ebenfalls angegeben.

5. Websiteübergreifendes Prefetching aktivieren
Beim Prefetching werden Abrufe frühzeitig gestartet, sodass die Bytes bereits im Browser sind, wenn der Nutzer navigiert. Dadurch wird die Navigation beschleunigt. So lassen sich Core Web Vitals ganz einfach verbessern und ein Teil der Netzwerkaktivität vor der Navigation auslagern. Dadurch wird der Largest Contentful Paint (LCP) direkt beschleunigt und es bleibt mehr Spielraum für First Input Delay (FID) und Cumulative Layout Shift (CLS) bei der Navigation.
Der Private Prefetch Proxy ermöglicht das Vorabrufen von Inhalten von verschiedenen Websites, ohne dass private Informationen über den Nutzer an den Zielserver weitergegeben werden.

Websiteübergreifendes Prefetching mit Private Prefetch Proxy aktivieren
Websiteinhaber behalten die Kontrolle über das Prefetching über eine bekannte traffic-advice-Ressource, analog zu /robots.txt für Webcrawler. Damit kann ein HTTP-Server deklarieren, dass implementierende Agents die entsprechenden Empfehlungen anwenden sollen. Derzeit können Websiteinhaber dem Agenten mitteilen, dass Netzwerkverbindungen nicht zugelassen oder gedrosselt werden sollen. In Zukunft werden möglicherweise weitere Empfehlungen hinzugefügt.
So hosten Sie eine Ressource mit Verkehrsinformationen:
- Fügen Sie diese JSON-ähnliche Datei hinzu:
public/.well-known/traffic-advice
[
{
"user_agent": "prefetch-proxy",
"google_prefetch_proxy_eap": {
"fraction": 1
}
}
]
Das Feld google_prefetch_proxy_eap ist ein spezielles Feld für das Early-Access-Programm und das Feld fraction dient dazu, den Anteil der angeforderten Prefetch-Vorgänge zu steuern, die der Private Prefetch Proxy sendet.
Verkehrshinweise sollten mit dem MIME-Typ application/trafficadvice+json zurückgegeben werden.
- Konfigurieren Sie den Antwortheader in der Datei
next.config.js:
next.config.js
const nextConfig = {
// Codelab: Modify content-type for traffic advice file.
async headers() {
return [
{
source: '/.well-known/traffic-advice',
headers: [
{
key: 'Content-Type',
value: 'application/trafficadvice+json',
},
],
},
]
},
}
module.exports = nextConfig
6. Shared Element Transitions API einbinden
Wenn ein Nutzer im Web von einer Seite zur nächsten navigiert, ändert sich der angezeigte Inhalt plötzlich und unerwartet, da die erste Seite verschwindet und die neue Seite angezeigt wird. Diese sequenzielle, nicht zusammenhängende Nutzererfahrung ist verwirrend und führt zu einer höheren kognitiven Belastung, da der Nutzer sich zusammenreimen muss, wie er an den aktuellen Ort gelangt ist. Außerdem wird dadurch die wahrgenommene Ladezeit der Seite erhöht, während Nutzer auf das Laden des gewünschten Ziels warten.
Flüssige Ladeanimationen verringern die kognitive Belastung, da Nutzer beim Navigieren zwischen den Seiten im Kontext bleiben. Außerdem wird die wahrgenommene Latenz beim Laden reduziert, da Nutzer in der Zwischenzeit etwas Interessantes und Ansprechendes sehen. Aus diesen Gründen bieten die meisten Plattformen benutzerfreundliche Primitiven, mit denen Entwickler nahtlose Übergänge erstellen können, z. B. Android, iOS, MacOS und Windows.
Die Shared Element Transitions API bietet Entwicklern dieselbe Funktion im Web, unabhängig davon, ob die Übergänge dokumentübergreifend (MPA) oder innerhalb eines Dokuments (SPA) erfolgen.

So integrieren Sie die Shared Element Transitions API für den SPA-Teil der Beispiel-App:
- Erstellen Sie einen benutzerdefinierten Hook, um den Übergang in der Datei
use-page-transition.jszu verwalten:
utils/use-page-transition.js
import { useEffect, useContext, useRef, useCallback } from 'react'
import ResourceContext from '../components/resource-context'
// Call this hook on this first page before you start the page transition. For Shared Element Transitions, you need to call the transition.start() method before the next page begins to render, and you need to do the Document Object Model (DOM) modification or setting of new shared elements inside the callback so that this hook returns the promise and defers to the callback resolve.
export const usePageTransitionPrep = () => {
const { dispatch } = useContext(ResourceContext)
return (elm) => {
const sharedElements = elm.querySelectorAll('.shared-element')
// Feature detection
if (!document.createDocumentTransition) {
return null
}
return new Promise((resolve) => {
const transition = document.createDocumentTransition()
Array.from(sharedElements).forEach((elm, idx) => {
transition.setElement(elm, `target-${idx}`)
})
transition.start(async () => {
resolve()
await new Promise((resolver) => {
dispatch({ type: 'update', transition: { transition, resolver } })
})
})
})
}
}
// Call this hook on the second page. Inside the useEffect hook, you can refer to the actual DOM element and set them as shared elements with the transition.setElement() method. When the resolver function is called, the transition is initiated between the captured images and newly set shared elements.
export const usePageTransition = () => {
const { state, dispatch } = useContext(ResourceContext)
const ref = useRef(null)
const setRef = useCallback((node) => {
ref.current = node
}, [])
useEffect(() => {
if (!state.transition || !ref.current) {
return
}
const { transition, resolver } = state.transition
const sharedElements = ref.current.querySelectorAll('.shared-element')
Array.from(sharedElements).forEach((elm, idx) => {
transition.setElement(elm, `target-${idx}`)
})
resolver()
return () => {
dispatch({ type: 'update', transition: null })
}
})
return setRef
}
- Rufen Sie den benutzerdefinierten Hook
usePageTransitionPrep()auf der Listenseite auf und rufen Sie dann die asynchrone Funktion auf, um die Methodetransition.start()imclick-Ereignis auszulösen.
Innerhalb der Funktion werden die Elemente der shared-element-Klasse erfasst und als gemeinsame Elemente registriert.
components/list-item.js
// Codelab: Add the Shared Element Transitions API.
import { usePageTransitionPrep } from '../utils/use-page-transition'
...
function ListItemForSPA({ item, href }) {
// Codelab: Add Shared Element Transitions.
const transitionNextState = usePageTransitionPrep()
const handleClick = async (e) => {
const elm = e.target.closest('a')
await transitionNextState(elm)
}
return (
<Link href={href}>
<a className='block flex items-center' onClick={handleClick}>
<Icon src={item.image} name={item.name} className='shared-element' />
<div className='text-xl'>{item.name}</div>
</a>
</Link>
)
}
- Rufen Sie auf der Detailseite den Hook
usePageTransition()auf, um die Callback-Funktiontransition.start()abzuschließen.
In diesem Callback werden auch die freigegebenen Elemente auf der Detailseite registriert.
pages/fruits/[name].js
// Codelab: Add the Shared Element Transitions API.
import { usePageTransition } from '../../utils/use-page-transition'
const Item = ({ data }) => {
const { name, image, amountPer, nutrition } = data
// Codelab: Add the Shared Element Transitions API.
const ref = usePageTransition()
return (
<div className={'flex flex-col items-center justify-center py-4 px-4 sm:flex-row'} ref={ref}>
<div className='flex flex-col items-center sm:w-2/4'>
<Image
className='object-cover border-gray-100 border-2 rounded-full shared-element'
src={image}
width='240'
height='240'
alt={`picture of ${name}`}
/>
<h1 className='text-4xl font-bold mt-4'>{name}</h1>
</div>
<div className='sm:w-2/4 w-full'>
<Nutrition amountPer={amountPer} nutrition={nutrition} />
</div>
</div>
)
...
}
Die Bildelemente werden jetzt auf den Listen- und Detailseiten geteilt und sind beim Seitenübergang nahtlos miteinander verbunden. Sie können die Animation sogar mit CSS-Pseudoelementen anpassen.

7. Glückwunsch
Glückwunsch! Sie haben eine sofort einsatzbereite und nahtlose Web-App mit einer reibungslosen, ansprechenden und intuitiven Benutzeroberfläche erstellt.
Weitere Informationen
Pre-Rendering
- Überarbeitetes Prerendering
- Sofortiges Laden von Seiten im Browser durch spekulatives Prerendering
- quicklink
bfcache
Websiteübergreifendes Prefetching
Signed Exchanges
Übergänge für Root- und freigegebene Elemente
- Übergänge für gemeinsame Elemente
- Reibungslose und einfache Seitenübergänge mit der Shared Element Transitions API
Diese APIs befinden sich noch in der frühen Entwicklungsphase. Bitte geben Sie uns Feedback unter crbug.com oder als Probleme im GitHub-Repository der entsprechenden APIs.