1. Başlamadan önce
Bu codelab'de, Google Chrome'un yerel olarak desteklediği en yeni API'lere sahip örnek bir web uygulamasına anında gezinme ve sorunsuz sayfa geçişlerinin nasıl ekleneceği açıklanmaktadır.
Örnek web uygulaması, popüler meyve ve sebzelerin besin değerlerini kontrol eder. Meyve listesi ve meyve ayrıntıları sayfaları tek sayfalık uygulama (SPA) olarak, sebze listesi ve sebze ayrıntıları sayfaları ise geleneksel çok sayfalı uygulama (MPA) olarak geliştirildi.
Özellikle, anında gezinme için önceden oluşturma, geri-ileri önbellek (bfcache) ve Özel Önceden Getirme Proxy'sini, sorunsuz sayfa geçişleri için de kök/paylaşılan öğe geçişlerini uygularsınız. MPA sayfaları için önceden işleme ve bfcache, SPA sayfaları için ise paylaşılan öğe geçişleri uygularsınız.
Site hızı, kullanıcı deneyiminin her zaman önemli bir unsurudur. Bu nedenle Google, gerçek dünyadaki kullanıcı deneyimini ölçmek için web sayfalarının yükleme performansını, etkileşimi ve görsel kararlılığını ölçen bir grup metrik olan Core Web Vitals'ı kullanıma sundu. En son API'ler, özellikle yük performansı açısından web sitenizin Core Web Vitals puanını sahada artırmanıza yardımcı olur.
Mindvalley'den tanıtım
Kullanıcılar, mobil yerel uygulamalarda gezinme ve durum değişikliklerinin son derece sezgisel olmasını sağlayan geçişler kullanmaya da alışkındır. Maalesef bu tür kullanıcı deneyimlerini web'de kopyalamak kolay değildir. Mevcut web platformu API'leriyle benzer etkiler elde edebilirsiniz ancak geliştirme, özellikle Android veya iOS uygulamalarındaki özellik benzerleriyle karşılaştırıldığında çok zor veya karmaşık olabilir. Kolay API'ler, uygulama ve web arasındaki bu kullanıcı ve geliştirici deneyimi boşluğunu doldurmak için tasarlanmıştır.
pixiv ve Tokopedia'dan demolar
Ön koşullar
Şu konuda bilgi:
- HTML
- CSS
- JavaScript
- Google Chrome Geliştirici Araçları
Öğrenecekleriniz:
Nasıl uygulanır?
- Önceden işleme
- önbellek
- Özel Önceden Getirme Proxy'si
- Kök/paylaşılan öğe geçişleri
Neler oluşturacaksınız?
En son hazır ve sorunsuz tarayıcı özellikleriyle zenginleştirilmiş, Next.js ile oluşturulmuş örnek bir web uygulaması:
- Önceden işleme ile neredeyse anında gezinme
- Tarayıcının geri ve ileri düğmeleriyle anında yüklemeler için bfcache
- Özel Önceden Getirme Proxy'si veya imzalı takas (SXG) ile kaynaklar arası gezinmeden elde edilen çok iyi ilk gösterimler
- Kök/paylaşılan öğeler geçişi olan sayfalar arasında sorunsuz geçiş
Gerekenler
- Chrome 101 veya sonraki sürümler
2. Başlayın
Chrome flag'lerini etkinleştir
- about://flags sayfasına gidip
Prerender2
vedocumentTransition API
çalışma zamanı işaretlerini etkinleştirin. - Tarayıcınızı yeniden başlatın.
Kodu alın
- Bu GitHub deposundaki kodu, favori geliştirme ortamınızda açın:
git clone -b codelab git@github.com:googlechromelabs/instant-seamless-demo.git
- Sunucuyu çalıştırmak için gereken bağımlılıkları yükleyin:
npm install
- 3000 numaralı bağlantı noktasından sunucuyu başlatın:
npm run dev
- Tarayıcınızda http://localhost:3000 adresine gidin.
Artık uygulamanızı düzenleyebilir ve iyileştirebilirsiniz. Her değişiklik yaptığınızda uygulama yeniden yüklenir ve değişiklikleriniz doğrudan görünür.
3. Önceden işlemeyi entegre etme
Bu demoda, örnek uygulamadaki bitki ayrıntıları sayfasının yükleme süresi, sunucu tarafındaki rastgele bir gecikme nedeniyle çok yavaştır. Önceden işleme ile bu bekleme süresini ortadan kaldırırsınız.
Sebze listesi sayfasına önceden işleme düğmeleri eklemek ve kullanıcı tıkladıktan sonra önceden işlemeyi tetiklemelerine izin vermek için:
- speculation-rules komut dosyası etiketini dinamik bir şekilde yerleştiren bir düğme bileşeni oluşturun:
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>
)
}
list-item.js
dosyasındakiPrerenderButton
bileşenini içe aktarın.
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>
)
}
- Spekülasyon Kuralları API'si eklemek için bir bileşen oluşturun.
Uygulama, prerenderURL
durumunu güncellediğinde SpeculationRules
bileşeni, sayfaya dinamik olarak bir komut dosyası etiketi ekler.
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])
}
- Bileşenleri uygulamayla entegre edin.
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
- Prerender'ı (Önceden oluştur) tıklayın.
Yüklemede önemli iyileşmeler görebilirsiniz. Gerçek kullanım örneğinde, önceden işleme bazı buluşsal yöntemler tarafından kullanıcının ziyaret etme olasılığı bulunan sayfa için tetiklenir.
Analiz
Varsayılan olarak, DOMContentLoaded
etkinliği gerçekleştiğinde örnek web uygulamasındaki analytics.js
dosyası bir sayfa görüntüleme etkinliği gönderir. Bu etkinlik önceden işleme aşamasında tetiklendiği için maalesef bu mantıklı değildir.
Bu sorunu düzeltmek amacıyla bir document.prerendering
ve prerenderingchange
etkinliğini tanıtmak için:
analytics.js
dosyasını yeniden yazın:
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()
})
...
Harika. Analizlerinizi, önceden işlemeyle uyumlu olacak şekilde başarıyla değiştirdiniz. Artık tarayıcı konsolunda doğru zamanlamayla sayfa görüntüleme günlüklerini görebilirsiniz.
4. Bfcache engelleyicileri kaldırın
unload
etkinlik işleyicisini kaldır
Gereksiz bir unload
etkinliğinin olması, artık önerilmez ve çok yaygın olarak yapılan bir hatadır. Bu, yalnızca bfcache'in çalışmasını önlemekle kalmaz, aynı zamanda güvenilir değildir. Örneğin, mobil cihazlarda ve Safari'de her zaman tetiklenmez.
unload
etkinliği yerine, unload
etkinliğinin tetiklendiği her durumda ve bir sayfa bfcache'ye yerleştirildiğinde tetiklenen pagehide
etkinliğini kullanırsınız.
unload
etkinlik işleyicisini kaldırmak için:
analytics.js
dosyasında,unload
etkinlik işleyicisinin kodunupagehide
etkinlik işleyicisinin koduyla değiştirin:
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')
})
Cache-control başlığını güncelleme
Cache-control: no-store
HTTP başlığı ile sunulan sayfalar tarayıcının bfcache özelliğinden yararlanamaz. Bu nedenle bu başlıkla ilgili tutumlu olmak iyi bir uygulamadır. Özellikle, sayfada oturum açma durumu gibi kişiselleştirilmiş veya önemli bilgiler yoksa sayfayı muhtemelen Cache-control: no-store
HTTP başlığıyla sunmanız gerekmez.
Örnek uygulamanın önbellek kontrolü başlığını güncellemek için:
getServerSideProps
kodunu değiştirin:
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/sebzeler/[ad].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')
...
Bir sayfanın bfcache'den geri yüklenip yüklenmediğini belirleyin
pageshow
etkinliği, sayfa ilk yüklendiğinde load
etkinliğinden hemen sonra ve sayfa bfcache'den geri yüklendiğinde tetiklenir. pageshow
etkinliğinin persisted
özelliği vardır. Bu özellik, sayfa bfcache'den geri yüklendiyse doğru, değilse false (yanlış) değerini alır. Normal sayfa yüklemelerini bfcache geri yüklemelerinden ayırt etmek için persisted
özelliğini kullanabilirsiniz. Önde gelen analiz hizmetleri bfcache'den haberdar olmalıdır. Ancak sayfanın bfcache'den geri yüklenip geri yüklenmediğini kontrol edebilir ve etkinlikleri manuel olarak gönderebilirsiniz.
Bir sayfanın bfcache'den geri yüklenip yüklenmediğini belirlemek için:
- Bu kodu
analytics.js
dosyasına ekleyin.
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()
}
})
Web sayfalarında hata ayıklama
Chrome Geliştirici Araçları, sayfalarınızın bfcache için optimize edildiğinden emin olmak ve uygun olmamalarına neden olabilecek sorunları belirlemenize yardımcı olabilir.
Belirli bir sayfayı test etmek için:
- Chrome'da sayfaya gidin.
- Chrome Geliştirici Araçları'nda Uygulama > Geri-İleri Önbellek > Testi Çalıştırın.
Chrome Geliştirici Araçları, sayfanın bfcache'den geri yüklenip yüklenemeyeceğini belirlemek için sayfadan çıkıp daha sonra geri dönmeyi dener.
İşlem başarılı olursa panel size sayfanın geri-ileri önbellekten geri yüklendiğini bildirir:
Başarısız olmanız durumunda panelde, sayfanın geri yüklenmediği ve bunun nedeni belirtilir. Bu durumun nedeni geliştirici olarak ele alabileceğiniz bir konuysa panel bunu da belirtir.
5. Siteler arası önceden getirme özelliğini etkinleştir
Önceden getirme işlemi erken başlar. Böylece, kullanıcı gezindiğinde baytlar zaten tarayıcıda yer alır. Bu da gezinmeyi hızlandırır. Bu, Core Web Vitals'ı iyileştirmenin ve bazı ağ etkinliklerini gezinmeden önce dengelemenin kolay bir yoludur. Bu, Largest Contentful Paint (LCP) değerini doğrudan hızlandırır ve gezinme sonrasında First Input Delay (FID) ve Cumulative Layout Shift (CLS) için daha fazla yer açar.
Özel Önceden Getirme Proxy'si, siteler arası önceden getirme özelliğini etkinleştirir ancak kullanıcıyla ilgili gizli bilgileri hedef sunucuya ifşa etmez.
Özel Önceden Getirme Proxy'si ile siteler arası önceden getirmeyi etkinleştirme
Web sitesi sahipleri, web tarayıcıları için /robots.txt
benzeri, iyi bilinen bir trafik tavsiyesi kaynağı aracılığıyla önceden getirme kontrolünü elinde tutar. Bu kaynak, bir HTTP sunucusunun, aracıların ilgili tavsiyeyi uygulaması gerektiğini bildirmesine olanak tanır. Şu anda web sitesi sahipleri, temsilciye ağ bağlantılarına izin vermemesini veya bağlantıları kısmasını önerebilir. Gelecekte başka öneriler de eklenebilir.
Trafik tavsiyesi kaynağı barındırmak için:
- Şu JSON benzeri dosyayı ekleyin:
public/.well-known/traffic-advice
[
{
"user_agent": "prefetch-proxy",
"google_prefetch_proxy_eap": {
"fraction": 1
}
}
]
google_prefetch_proxy_eap
alanı, erken erişim programı için özel bir alandır. fraction
alanı ise Özel Önceden Getirme Proxy'sinin gönderdiği istenen önceden getirmelerin oranını kontrol eden bir alandır.
Trafik önerisi application/trafficadvice+json
MIME türüyle döndürülür.
next.config.js
dosyasında yanıt başlığını yapılandırın:
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'yi entegre edin
Kullanıcı web'de bir sayfadan bir başkasına geçtiğinde, ilk sayfa kaybolup yeni sayfa görüntülenirken kullanıcının gördüğü içerik aniden ve beklenmedik bir şekilde değişir. Bu sıralı, bağlantısız kullanıcı deneyimi kafa karıştırıcıdır ve kullanıcı bulunduğu yere nasıl gideceğini bir araya getirmek zorunda kalır. Bu da, bilişsel yüklenme düzeyinin artmasına yol açar. Ayrıca bu deneyim, kullanıcıların istenen hedefin yüklenmesini beklerken sayfanın yüklendiğini algılama oranını artırır.
Sorunsuz yükleme animasyonları, kullanıcılar sayfalar arasında gezinirken bağlam içinde kaldığı için bilişsel yükü azaltır ve aynı zamanda ilgi çekici ve keyifli bir şey gördüğü için yüklemenin algılanan gecikmesini azaltır. Bu nedenle çoğu platform, geliştiricilerin sorunsuz geçişler oluşturmasına olanak tanıyan, kullanımı kolay temel öğeler sağlar (ör. Android, iOS, MacOS ve Windows).
Shared Element Transitions API, geçişlerin dokümanlar arası (MPA) veya doküman içi (SPA) olmasına bakılmaksızın, geliştiricilere web'de aynı özelliği sunar.
pixiv ve Tokopedia'dan demolar
Örnek uygulamanın SPA bölümü için Shared Element Transitions API'yi entegre etmek üzere:
use-page-transition.js
dosyasındaki geçişi yönetmek için özel bir kanca oluşturun:
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
}
- Liste sayfasında
usePageTransitionPrep()
özel kancasını çağırın ve ardındanclick
etkinliği içindetransition.start()
yöntemini tetiklemek için eş zamansız işlevini çağırın.
İşlevin içinde shared-element
sınıf öğeleri toplanır ve paylaşılan öğeler olarak kaydedilir.
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>
)
}
- Ayrıntılar sayfasında,
transition.start()
geri çağırma işlevini bitirmek içinusePageTransition()
kancasını çağırın.
Bu geri çağırmada, ayrıntı sayfasındaki paylaşılan öğeler de kaydedilir.
sayfalar/meyveler/[ad].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>
)
...
}
Artık resim öğelerinin liste ve ayrıntı sayfalarında paylaşıldığını ve sayfa geçişinde sorunsuz bir şekilde bağlandığını görebilirsiniz. Hatta CSS sözde öğeleriyle daha ilgi çekici hale getirmek için animasyonu özelleştirebilirsiniz.
7. Tebrikler
Tebrikler! Kolayca, ilgi çekici ve sezgisel bir kullanıcı deneyimi sunan hazır ve sorunsuz bir web uygulaması oluşturdunuz.
Daha fazla bilgi
Önceden işleme
- Önceden oluşturma, yenilendi
- Tahmine dayalı önceden işleme ile anında sayfa yüklemelerini tarayıcıya getirme
- hızlı bağlantı
önbellek
Siteler arası önceden getirme
İmzalı takaslar
Kök/paylaşılan öğe geçişleri
Bu API'ler hâlâ geliştirmenin ilk aşamalarında olduğundan geri bildiriminizi lütfen crbug.com adresinden veya sorun olarak ilgili API'lerin GitHub deposunda paylaşın.