1. Sebelum memulai
Codelab ini mengajarkan cara menambahkan navigasi instan dan transisi halaman yang lancar ke aplikasi web contoh dengan API terbaru yang didukung Google Chrome secara native.
Aplikasi web contoh memeriksa nilai gizi buah dan sayuran populer. Halaman daftar buah dan detail buah dibuat sebagai aplikasi halaman tunggal (SPA), dan halaman daftar sayuran serta detail sayuran dibuat sebagai aplikasi beberapa halaman tradisional (MPA).
Secara khusus, Anda menerapkan pra-rendering, back-forward cache (bfcache), dan Proxy Pengambilan Data Pribadi untuk navigasi instan, dan transisi elemen bersama/root untuk transisi halaman yang lancar. Anda menerapkan pra-rendering dan bfcache untuk halaman MPA, dan transisi elemen bersama untuk halaman SPA.
Kecepatan situs selalu menjadi aspek penting pengalaman pengguna, itulah sebabnya Google memperkenalkan Data Web Inti, kumpulan metrik yang mengukur performa pemuatan, interaktivitas, dan stabilitas visual halaman web untuk mengukur informasi pengalaman pengguna di seluruh dunia. API terbaru membantu Anda meningkatkan skor Data Web Inti situs Anda di lapangan, terutama untuk performa pemuatan.
Demo dari Mindvalley
Pengguna juga terbiasa dengan penggunaan transisi untuk membuat navigasi dan perubahan status sangat intuitif di aplikasi native seluler. Sayangnya, replikasi pengalaman pengguna tersebut di web tidaklah jelas. Meskipun Anda mungkin dapat mencapai efek yang serupa dengan API platform web saat ini, pengembangan mungkin terlalu sulit atau rumit, terutama jika dibandingkan dengan fitur serupa pada aplikasi Android atau iOS. API yang lancar didesain untuk mengisi kesenjangan pengalaman pengguna dan developer antara aplikasi dan web.
Prasyarat
Pengetahuan tentang:
- HTML
- CSS
- JavaScript
- Google Chrome Developer Tools
Yang akan Anda pelajari:
Cara menerapkan:
- Pra-rendering
- bfcache
- Proxy Pengambilan Data Pribadi
- Transisi elemen root/bersama
Yang akan Anda buat
Aplikasi web contoh yang dibuat dengan Next.js yang diperkaya dengan kemampuan browser instan dan lancar terbaru:
- Navigasi yang nyaris seketika dengan pra-rendering
- bfcache untuk pemuatan instan dengan tombol maju dan mundur browser
- Kesan pertama yang memuaskan dari navigasi lintas asal dengan Proxy Pengambilan Data Pribadi atau Signed HTTP Exchange (SXG)
- Transisi yang lancar antara halaman dengan transisi elemen root/bersama
Yang Anda perlukan
- Chrome versi 101 atau yang lebih baru
2. Memulai
Mengaktifkan tanda Chrome
- Buka about://flags, lalu aktifkan tanda runtime
Prerender2
dandocumentTransition API
. - Mulai ulang browser.
Mendapatkan kode
- Buka kode dari repositori GitHub ini di lingkungan pengembangan favorit Anda:
git clone -b codelab git@github.com:googlechromelabs/instant-seamless-demo.git
- Instal dependensi yang diperlukan untuk menjalankan server:
npm install
- Mulai server pada port 3000:
npm run dev
- Buka http://localhost:3000 di browser Anda.
Sekarang Anda dapat mengedit dan meningkatkan aplikasi kualitas. Setiap kali Anda melakukan perubahan, aplikasi akan dimuat ulang dan perubahannya akan langsung terlihat.
3. Mengintegrasikan pra-rendering
Untuk tujuan demo ini, waktu pemuatan halaman detail sayuran di aplikasi contoh sangat lambat karena terjadi keterlambatan arbitrer di sisi server. Anda menghapus waktu tunggu ini dengan pra-rendering.
Untuk menambahkan tombol pra-render ke halaman daftar sayuran, biarkan tombol tersebut memicu pra-rendering setelah pengguna mengklik:
- Buat komponen tombol, yang menyisipkan tag skrip aturan spekulasi secara dinamis:
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>
)
}
- Impor komponen
PrerenderButton
di filelist-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>
)
}
- Buat komponen untuk menambahkan Speculation Rules API.
Komponen SpeculationRules
akan menyisipkan tag skrip secara dinamis ke dalam halaman saat aplikasi memperbarui status prerenderURL
.
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])
}
- Integrasikan komponen dengan aplikasi.
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
- Klik Pra-render.
Sekarang Anda dapat melihat pengoptimalan pemuatan yang signifikan. Dalam kasus penggunaan yang sebenarnya, pra-rendering dipicu untuk halaman yang kemungkinan dikunjungi pengguna berikutnya menurut sejumlah heuristik.
Analisis
Secara default, file analytics.js
di aplikasi web contoh mengirimkan peristiwa kunjungan halaman saat peristiwa DOMContentLoaded
terjadi. Sayangnya, tindakan ini kurang tepat karena peristiwa ini dilepaskan selama fase pra-rendering.
Untuk memperkenalkan peristiwa document.prerendering
dan prerenderingchange
guna memperbaiki masalah ini:
- Tulis ulang file
analytics.js
:
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()
})
...
Bagus. Anda berhasil mengubah analisis sehingga kompatibel dengan pra-rendering. Sekarang Anda dapat melihat log kunjungan halaman dengan waktu yang tepat di konsol browser.
4. Menghapus pemblokir bfcache
Menghapus pengendali peristiwa unload
Peristiwa unload
yang tidak diperlukan adalah kesalahan yang sangat umum. Hal ini tidak disarankan lagi. Tidak hanya mencegah bfcache agar berfungsi, tetapi peristiwa ini juga tidak dapat diandalkan. Misalnya, peristiwa ini tidak selalu aktif di seluler dan Safari.
Daripada menggunakan peristiwa unload
, gunakan peristiwa pagehide
, yang aktif di semua kasus saat peristiwa unload
aktif dan saat halaman dimasukkan ke dalam bfcache.
Untuk menghapus pengendali peristiwa unload
:
- Di file
analytics.js
, ganti kode untuk pengendali peristiwaunload
dengan kode untuk pengendali peristiwapagehide
:
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')
})
Memperbarui header kontrol cache
Halaman yang ditayangkan dengan header HTTP Cache-control: no-store
tidak mendapatkan manfaat dari fitur bfcache browser, sehingga sebaiknya Anda menghemat waktu dengan header ini. Khususnya, jika halaman tidak berisi informasi yang dipersonalisasi atau informasi penting, seperti status login, Anda mungkin tidak perlu menayangkannya dengan header HTTP Cache-control: no-store
.
Untuk memperbarui header kontrol cache dari aplikasi contoh:
- Ubah kode
getServerSideProps
:
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')
...
Menentukan apakah halaman dipulihkan dari bfcache
Peristiwa pageshow
aktif tepat setelah peristiwa load
, saat halaman pertama kali dimuat dan setiap kali halaman dipulihkan dari bfcache. Peristiwa pageshow
memiliki properti persisted
, yang benar jika halaman dipulihkan dari bfcache dan salah jika tidak. Anda dapat menggunakan properti persisted
untuk membedakan pemuatan halaman reguler dari pemulihan bfcache. Layanan analisis utama harus mengetahui tentang bfcache, tetapi Anda dapat memeriksa apakah halaman dipulihkan dari bfcache dan mengirim peristiwa secara manual.
Untuk menentukan apakah halaman dipulihkan dari bfcache:
- Tambahkan kode ini ke file
analytics.js
.
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()
}
})
Men-debug halaman web
Chrome Developer Tools dapat membantu Anda menguji halaman guna memastikan bahwa halaman dioptimalkan untuk bfcache dan mengidentifikasi masalah yang dapat membuatnya tidak memenuhi syarat.
Untuk menguji halaman tertentu:
- Buka halaman di Chrome.
- Di Chrome Developer Tools, klik Application > Back-forward Cache > Run Test.
Chrome Developer Tools berusaha menutup lalu membuka untuk menentukan apakah halaman dapat dipulihkan dari bfcache.
Jika berhasil, panel akan memberi tahu Anda bahwa halaman dipulihkan dari back-forward cache:
Jika tidak berhasil, panel akan memberi tahu Anda bahwa halaman tidak dipulihkan beserta alasannya. Jika alasannya adalah sesuatu yang dapat Anda tangani sebagai developer, panel juga akan menyampaikannya kepada Anda.
5. Mengaktifkan pengambilan data lintas situs
Pengambilan data dimulai lebih awal sehingga byte sudah ada di browser saat pengguna melakukan navigasi, sehingga mempercepat navigasi. Ini adalah cara mudah untuk meningkatkan Data Web Inti dan mengimbangi beberapa aktivitas jaringan sebelum navigasi. Tindakan ini secara langsung mempercepat Largest Contentful Paint (LCP), dan memberikan lebih banyak ruang untuk First Input Delay (Penundaan Input Pertama) (FID) dan Cumulative Layout Shift (Pergeseran Tata Letak Kumulatif) (CLS) setelah navigasi.
Proxy Pengambilan Data Pribadi memungkinkan pengambilan data lintas situs, namun tidak mengungkapkan informasi pribadi tentang pengguna ke server tujuan.
Mengaktifkan pengambilan data lintas situs dengan Proxy Pengambilan Data Pribadi
Pemilik situs mempertahankan kontrol pengambilan data melalui resource traffic-advice yang sudah diketahui, serupa dengan /robots.txt
untuk web crawler, yang memungkinkan server HTTP mendeklarasikan bahwa agen penerapan harus menerapkan saran yang sesuai. Saat ini, pemilik situs dapat menyarankan agen untuk melarang atau membatasi koneksi jaringan. Di masa mendatang, saran lain mungkin ditambahkan.
Untuk menghosting resource traffic-advice:
- Tambahkan file mirip JSON ini:
public/.well-known/traffic-advice
[
{
"user_agent": "prefetch-proxy",
"google_prefetch_proxy_eap": {
"fraction": 1
}
}
]
Kolom google_prefetch_proxy_eap
adalah kolom khusus untuk program akses awal dan kolom fraction
adalah kolom untuk mengontrol fraksi pengambilan data yang diminta yang dikirim oleh Proxy Pengambilan Data Pribadi.
Saran traffic harus ditampilkan dengan jenis MIME application/trafficadvice+json
.
- Pada file
next.config.js
, konfigurasikan header respons:
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. Mengintegrasikan Shared Element Transitions API
Saat pengguna menavigasi di web dari satu halaman ke halaman lainnya, konten yang mereka lihat berubah secara tiba-tiba dan tidak terduga saat halaman pertama menghilang dan halaman baru muncul. Pengalaman pengguna yang terputus dan berurutan ini membingungkan dan berujung pada beban kognitif yang lebih tinggi karena pengguna terpaksa menggabungkan cara mencapai ke lokasi mereka. Selain itu, pengalaman ini meningkatkan seberapa banyak pengguna merasakan pemuatan halaman saat menunggu tujuan yang diinginkan untuk dimuat.
Animasi pemuatan yang lancar menurunkan pemuatan kognitif karena pengguna tetap berada dalam konteks saat mereka berpindah antar-halaman, dan mengurangi latensi pemuatan yang dirasakan karena sementara itu pengguna melihat sesuatu yang menarik dan menyenangkan. Karena alasan ini, sebagian besar platform menyediakan primitif yang mudah digunakan yang memungkinkan developer membuat transisi yang lancar, seperti Android, iOS, MacOS, dan Windows.
Shared Element Transitions API memberi developer kemampuan yang sama di web, terlepas dari apakah transisi lintas dokumen (MPA) atau intra-dokumen (SPA).
Untuk mengintegrasikan Shared Element Transitions API untuk bagian SPA aplikasi contoh:
- Buat hook kustom untuk mengelola transisi dalam file
use-page-transition.js
:
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
}
- Panggil hook kustom
usePageTransitionPrep()
di halaman daftar, lalu panggil fungsi asinkron untuk memicu metodetransition.start()
di dalam peristiwaclick
.
Di dalam fungsi, elemen class shared-element
dikumpulkan dan didaftarkan sebagai elemen bersama.
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>
)
}
- Pada halaman detail, panggil hook
usePageTransition()
untuk menyelesaikan fungsi callbacktransition.start()
.
Dalam callback ini, elemen bersama di halaman detail juga terdaftar.
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>
)
...
}
Sekarang Anda dapat melihat bahwa elemen gambar dibagikan di halaman daftar dan detail, serta terhubung dengan lancar dalam transisi halaman. Anda bahkan dapat menyesuaikan animasi agar lebih menarik dengan elemen pseudo CSS.
7. Selamat
Selamat! Anda telah membuat aplikasi web yang cepat dan lancar dengan pengalaman pengguna yang lancar, menarik, dan intuitif.
Pelajari lebih lanjut
Pra-rendering
- Pra-rendering, diperbarui
- Memberikan pemuatan halaman instan ke browser melalui pra-rendering spekulatif
- quicklink
bfcache
Pengambilan data lintas situs
Signed HTTP Exchange
Transisi elemen root/bersama
- Transisi Elemen Bersama
- Transisi halaman yang lancar dan sederhana dengan Shared Element Transitions API
API ini masih dalam tahap awal pengembangan, jadi sampaikan masukan Anda di crbug.com atau sebagai masalah di repositori GitHub API terkait.