۱. قبلاز شروع
این codelab به شما آموزش میدهد که چگونه بااستفاده از جدیدترین میاناهای برنامهسازی کاربردی که Google Chrome بهطور بومی از آنها پشتیبانی میکند، ناوبری فوری و گذارهای صفحه یکپارچه را به یک برنامه وب نمونه اضافه کنید.
برنامه وب نمونه ارزش غذایی میوهها و سبزیجات محبوب را بررسی میکند. صفحههای فهرست میوهها و جزئیات میوهها بهعنوان برنامه تکصفحهای (SPA) ساخته شدهاند و صفحههای فهرست سبزیجات و جزئیات سبزیجات بهعنوان برنامه چندصفحهای سنتی (MPA) ساخته شدهاند.

بهطور دقیق، شما پیشپرداخت، حافظه نهان ناوبری بین صفحات (bfcache)، و پراکسی پیشواکشی خصوصی را برای ناوبری فوری، و انتقالهای عنصر ریشه/مشترک را برای انتقالهای صفحه یکپارچه پیادهسازی میکنید. پیشپردازش و حافظه نهان ناوبری بین صفحات را برای صفحههای MPA و انتقالهای عنصر مشترک را برای صفحههای SPA پیادهسازی میکنید.
سرعت سایت همیشه جنبه مهمی از تجربه کاربری است، به همین دلیل Google Core Web Vitals را معرفی کرد، مجموعهای از سنجهها که عملکرد بارگیری، تعامل، و پایداری بصری صفحههای وب را برای سنجش تجربه کاربری در دنیای واقعی اندازهگیری میکند. جدیدترین میاناهای برنامهسازی کاربردی به شما کمک میکنند امتیاز «معیارهای کلیدی اصلی وب» وبسایتتان را در این زمینه بهبود دهید، بهویژه برای عملکرد بارگیری.

نسخه نمایشی از Mindvalley
کاربران همچنین به استفاده از گذرها برای ایجاد پیمایشها و تغییرات وضعیت بسیار شهودی در برنامههای بومی تلفن همراه عادت دارند. متأسفانه تکرار چنین تجربههای کاربری در وب ساده نیست. اگرچه ممکن است بتوانید با میاناهای برنامهسازی کاربردی پلاتفرم وب فعلی به جلوههای مشابهی دست یابید، اما توسعه ممکن است بسیار دشوار یا پیچیده باشد، بهویژه در مقایسه با همتایان ویژگی در برنامههای Android یا iOS. میاناهای برنامهسازی کاربردی یکپارچه برای پر کردن این شکاف تجربه کاربر و توسعهدهنده بین برنامه و وب طراحی شدهاند.

نسخههای نمایشی از pixiv و Tokopedia
پیشنیازها
دانش:
- HTML
- CSS
- JavaScript
- ابزارهای توسعهدهنده Google Chrome
آنچه خواهید آموخت:
نحوه پیادهسازی:
- پیشپرداززنی
- bfcache
- پراکسی پیشواکشی خصوصی
- انتقالهای عنصر ریشه/همرسانیشده
آنچه خواهید ساخت
یک برنامه وب نمونه ساختهشده با Next.js که با جدیدترین قابلیتهای مرورگر فوری و یکپارچه غنی شده است:
- ناوبری تقریباً فوری با پیشپرداززنی
- حافظه نهان ناوبری بین صفحات برای بار کردن فوری با دکمههای عقب و جلو مرورگر
- تأثیر اولیه عالی از ناوبری میانمبدئی با «پراکسی پیشواکشی خصوصی» یا «مبادله امضاشده» (SXG)
- گذار یکپارچه بین صفحهها با گذار عناصر ریشه/مشترک
آنچه نیاز خواهید داشت
- Chrome نسخه ۱۰۱ یا بالاتر
۲. شروع کنید
فعال کردن پرچمهای Chrome
- به about://flags بروید، سپس پرچمهای زمان اجرای
Prerender2وdocumentTransition APIرا فعال کنید. - مرورگر را بازراهاندازی کنید.
دریافت کد
- کد را از این مخزن GitHub در محیط توسعه نرمافزار دلخواهتان باز کنید:
git clone -b codelab git@github.com:googlechromelabs/instant-seamless-demo.git
- وابستگیهای لازم برای اجرای سرور را نصب کنید:
npm install
- سرور را در درگاه ۳۰۰۰ راهاندازی کنید:
npm run dev
- در مرورگرتان به http://localhost:3000 بروید.
اکنون میتوانید برنامهتان را ویرایش و بهبود دهید. هروقت تغییراتی ایجاد کنید، برنامه مجدداً بار میشود و تغییراتتان مستقیماً قابلمشاهده است.
۳. ادغام کردن پیشپرداززنی
برای اهداف این نسخه نمایشی، زمان بار کردن صفحه vegetable-details در برنامه نمونه بهدلیل تأخیر دلخواه در سمت سرور بسیار کند است. با پیشپرداز کردن، این زمان انتظار را حذف میکنید.
برای افزودن دکمههای پیشپرداز به صفحه vegetable-list و اجازه دادن به آنها برای راهاندازی پیشپرداز پساز کلیک کاربر:
- یک مؤلفه دکمه ایجاد کنید که برچسب دستورگان speculation-rules را بهصورت پویا درج میکند:
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>
)
}
- عنصر
PrerenderButtonرا در فایلlist-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>
)
}
- مؤلفهای برای افزودن Speculation Rules API ایجاد کنید.
وقتی برنامه وضعیت prerenderURL را بهروز میکند، عنصر SpeculationRules بهطور پویا برچسب دستورگان را در صفحه درج میکند.
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])
}
- اجزا را با برنامه ادغام کنید.
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
- روی پیشپرداز کلیک کنید.
اکنون میتوانید بهبود قابلتوجه بارگیری را ببینید. در مورد استفاده واقعی، پیشپرداز برای صفحهای که کاربر احتمالاً بعداً از آن بازدید میکند با برخی روشهای اکتشافی راهاندازی میشود.

Analytics
بهطور پیشفرض، فایل analytics.js در برنامه وب نمونه، وقتی رویداد DOMContentLoaded اتفاق میافتد، رویداد بازدید صفحه ارسال میکند. متأسفانه این کار عاقلانه نیست زیرا این رویداد درطول مرحله پیشپرداز اجرا میشود.
برای معرفی رویداد document.prerendering و prerenderingchange برای رفع این مشکل:
- بازنویسی کردن فایل
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()
})
...
عالی است، تجزیهوتحلیلهایتان را باموفقیت اصلاح کردید تا با پیشپرداخت سازگار شوند. اکنون میتوانید گزارشهای بازدید صفحه را با زمانبندی درست در کنسول مرورگر ببینید.
۴. برداشتن مسدودکنندههای حافظه نهان ناوبری بین صفحات
برداشتن گرداننده رویداد unload
داشتن رویداد غیرضروری unload اشتباه بسیار رایجی است که دیگر توصیه نمیشود. این کار نهتنها مانع از کار کردن حافظه نهان ناوبری بین صفحات میشود، بلکه غیرقابلاعتماد هم هست. برای مثال، همیشه در تلفن همراه و Safari فعال نمیشود.
بهجای رویداد unload، از رویداد pagehide استفاده میکنید که در همه مواردی که رویداد unload فعال میشود و زمانی که صفحهای در حافظه نهان ناوبری بین صفحات قرار میگیرد، فعال میشود.
برای برداشتن گرداننده رویداد unload:
- در فایل
analytics.js، کد کنترلکننده رویدادunloadرا با کد کنترلکننده رویدادpagehideجایگزین کنید:
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')
})
بهروزرسانی سرایند کنترل حافظه نهان
صفحههایی که با سرایند HTTP Cache-control: no-store ارائه میشوند از ویژگی حافظه نهان ناوبری بین صفحات مرورگر بهرهمند نمیشوند، بنابراین بهتر است در استفاده از این سرایند صرفهجویی شود. بهویژه اگر صفحه حاوی اطلاعات شخصیسازیشده یا مهمی مثل وضعیت ورود به سیستم نباشد، احتمالاً نیازی نیست آن را با سرایند HTTP Cache-control: no-store ارائه دهید.
برای بهروزرسانی سرصفحه cache-control برنامه نمونه:
- کد
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')
...
تعیین اینکه آیا صفحه از حافظه نهان ناوبری بین صفحات بازیابی شده است یا نه
رویداد pageshow بلافاصله پساز رویداد load هنگام بار شدن اولیه صفحه و هر زمان که صفحه از حافظه نهان ناوبری بین صفحات بازیابی میشود، فعال میشود. رویداد pageshow دارای ویژگی persisted است که اگر صفحه از حافظه نهان ناوبری بین صفحات بازیابی شده باشد، مقدار آن درست است و اگر بازیابی نشده باشد، مقدار آن نادرست است. میتوانید از ویژگی persisted برای تشخیص بارگیریهای معمولی صفحه از بازیابیهای bfcache استفاده کنید. سرویسهای تجزیهوتحلیل اصلی باید از bfcache آگاه باشند، اما میتوانید بررسی کنید که آیا صفحه از bfcache بازیابی شده است یا نه و رویدادها را بهصورت دستی ارسال کنید.
برای تعیین اینکه آیا صفحهای از حافظه نهان ناوبری بین صفحات بازیابی شده است یا نه:
- این کد را به فایل
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()
}
})
اشکالزدایی صفحه وب
«ابزارهای توسعهدهنده Chrome» میتواند به شما کمک کند صفحههایتان را آزمایش کنید تا مطمئن شوید برای bfcache بهینهسازی شدهاند و هرگونه مشکلی را که ممکن است آنها را واجدشرایط نکند شناسایی کنید.
برای آزمایش صفحهای خاص:
- در Chrome به صفحه موردنظر بروید.
- در «ابزارهای توسعهدهنده Chrome»، روی Application (برنامه) > Back-forward Cache (حافظه نهان ناوبری بین صفحات) > Run Test (اجرای آزمایش) کلیک کنید.
«ابزارهای توسعهدهنده Chrome» تلاش میکند از صفحه خارج شود و سپس به آن برگردد تا مشخص کند آیا صفحه میتواند از حافظه نهان ناوبری بین صفحات بازیابی شود یا نه.

اگر موفقیتآمیز باشد، پانل به شما میگوید که صفحه از حافظه نهان ناوبری بین صفحات بازیابی شده است:

اگر ناموفق باشد، پانل به شما میگوید که صفحه بازیابی نشده است و دلیل آن را توضیح میدهد. اگر دلیل چیزی است که شما بهعنوان توسعهدهنده میتوانید آن را برطرف کنید، پانل این موضوع را نیز به شما میگوید.

۵. فعال کردن واکشی پیشاپیش بینسایتی
پیشواکشی، واکشی را زودتر شروع میکند تا وقتی کاربر پیمایش میکند، بایتها ازقبل در مرورگر باشند که این کار پیمایش را تسریع میکند. این روشی آسان برای بهبود Core Web Vitals و جبران برخیاز فعالیتهای شبکه پیشاز پیمایش است. این کار مستقیماً بزرگترین نقاشی دارای محتوا (LCP) را تسریع میکند و فضای بیشتری برای تأخیر اولین ورودی (FID) و تغییر چیدمان تجمعی (CLS) هنگام پیمایش فراهم میکند.
پراکسی واکشی اولیه خصوصی واکشی اولیه بینسایتی را فعال میکند، اما اطلاعات خصوصی کاربر را برای سرور مقصد فاش نمیکند.

فعال کردن واکشی پیشاپیش بینسایتی با «پراکسی واکشی پیشاپیش خصوصی»
مالکان وبسایت کنترل پیشواکشی را ازطریق منبع شناختهشده traffic-advice حفظ میکنند، مشابه /robots.txt برای خزندههای وب، که به سرور HTTP اجازه میدهد اعلام کند که عاملهای پیادهسازی باید توصیه مربوطه را اعمال کنند. درحالحاضر، مالکان وبسایت میتوانند به عامل توصیه کنند که اتصالهای شبکه را غیرمجاز یا محدود کند. در آینده، ممکن است توصیههای دیگری اضافه شود.
برای میزبانی منبع مشاوره ترافیک:
- این فایل JSON-مانند را اضافه کنید:
public/.well-known/traffic-advice
[
{
"user_agent": "prefetch-proxy",
"google_prefetch_proxy_eap": {
"fraction": 1
}
}
]
فیلد google_prefetch_proxy_eap فیلد ویژهای برای برنامه دسترسی زودرس است و فیلد fraction فیلدی برای کنترل کسری از واکشیهای پیشاپیش درخواستی است که «پراکسی واکشی پیشاپیش خصوصی» ارسال میکند.
توصیه ترافیک باید با نوع MIME application/trafficadvice+json برگردانده شود.
- در فایل
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
۶. ادغام کردن Shared Element Transitions API
وقتی کاربر در وب از صفحهای به صفحه دیگر میرود، محتوایی که میبیند بهطور ناگهانی و غیرمنتظره تغییر میکند، زیرا صفحه اول ناپدید میشود و صفحه جدید ظاهر میشود. این تجربه کاربری متوالی و قطعشده گیجکننده است و منجر به بار شناختی بالاتری میشود زیرا کاربر مجبور است تکههای مختلف را کنار هم بگذارد تا بفهمد چگونه به جایی که هست رسیده است. علاوهبراین، این تجربه باعث میشود کاربران درحین انتظار برای بار شدن مقصد موردنظر، بار شدن صفحه را بیشتر احساس کنند.
پویانماییهای بار کردن روان بار شناختی را کاهش میدهند زیرا کاربران هنگام پیمایش بین صفحهها در بافت باقی میمانند و تأخیر درکشده بار کردن را کاهش میدهند زیرا کاربران در این میان چیزی جذاب و لذتبخش میبینند. به این دلایل، اکثر پلاتفرمها، ازجمله Android، iOS، MacOS، و Windows، عناصر اولیهای با کاربرد آسان ارائه میدهند که به توسعهدهندگان امکان میدهد انتقالهای یکپارچه بسازند.
Shared Element Transitions API همین قابلیت را در وب برای توسعهدهندگان فراهم میکند، صرفنظر از اینکه انتقالها بینسندی (MPA) یا درونسندی (SPA) باشند.

نسخههای نمایشی از pixiv و Tokopedia
برای ادغام Shared Element Transitions API برای بخش SPA برنامه نمونه:
- یک قلاب سفارشی برای مدیریت انتقال در فایل
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
}
usePageTransitionPrep()قلاب سفارشی را در صفحه فهرست فراخوانی کنید و سپس تابع async را فراخوانی کنید تا روشtransition.start()را در رویدادclickراهاندازی کنید.
درون تابع، عناصر کلاس shared-element جمعآوری میشوند و بهعنوان عناصر مشترک ثبت میشوند.
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>
)
}
- در صفحه جزئیات، قلاب
usePageTransition()را فراخوانی کنید تا تابع بازخوانیtransition.start()تکمیل شود.
در این فراخوانی، عناصر همرسانیشده در صفحه جزئیات نیز ثبت میشوند.
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>
)
...
}
اکنون میتوانید ببینید که عناصر تصویر در صفحههای فهرست و جزئیات همرسانی شدهاند و بهطور یکپارچه در انتقال صفحه متصل شدهاند. حتی میتوانید پویانمایی را با شبهعناصر CSS سفارشیسازی کنید تا جذابتر شود.

۷. تبریک میگوییم
تبریک! برنامه وب فوری و یکپارچهای با تجربه کاربری آسان، جذاب، و شهودی ایجاد کردهاید.
بیشتر بدانید
پیشپرداززنی
bfcache
واکشی اولیه بینسایتی
مبادلههای امضاشده
انتقالهای عنصر ریشه/همرسانیشده
این API-ها هنوز در مراحل اولیه توسعه هستند، بنابراین لطفاً بازخوردتان را در crbug.com یا بهعنوان مشکل در مخزن Github مربوط به API-های مربوطه همرسانی کنید.