1. قبل از شروع
این لبه کد به شما می آموزد که چگونه ناوبری فوری و انتقال بدون درز صفحه را به یک برنامه وب نمونه با جدیدترین APIهایی که Google Chrome به طور بومی پشتیبانی می کند، اضافه کنید.
نمونه برنامه وب ارزش غذایی میوه ها و سبزیجات محبوب را بررسی می کند. صفحات فهرست میوه و جزئیات میوه به عنوان یک برنامه تک صفحه ای (SPA) و صفحات فهرست سبزیجات و جزئیات سبزیجات به عنوان یک برنامه سنتی چند صفحه ای (MPA) ساخته شده اند.
به طور خاص، شما پیش اجرا، حافظه پنهان عقب/ جلو (bfcache)، و پراکسی پیش واکشی خصوصی را برای پیمایش فوری، و انتقال عناصر ریشه/اشتراکگذاری شده برای انتقال بدون درز صفحه پیادهسازی میکنید. شما پیش اجرا و bfcache را برای صفحات MPA و انتقال عناصر مشترک را برای صفحات SPA پیاده سازی می کنید.
سرعت سایت همیشه یکی از جنبه های مهم تجربه کاربر است، به همین دلیل است که گوگل Core Web Vitals را معرفی کرد، مجموعه ای از معیارها که عملکرد بارگذاری، تعامل و ثبات بصری صفحات وب را برای سنجش تجربه کاربر در دنیای واقعی اندازه گیری می کند. آخرین APIها به شما کمک می کنند تا امتیاز Core Web Vitals وب سایت خود را در زمینه بهبود ببخشید، به خصوص برای عملکرد بارگذاری.
نسخه ی نمایشی از Mindvalley
کاربران همچنین به استفاده از انتقال برای ایجاد ناوبری و تغییرات حالت بسیار بصری در برنامه های بومی تلفن همراه عادت دارند. متأسفانه، تکرار چنین تجربیات کاربری در وب ساده نیست. در حالی که ممکن است بتوانید با API های پلتفرم وب فعلی به اثرات مشابهی دست یابید، توسعه ممکن است بسیار دشوار یا پیچیده باشد، به خصوص زمانی که با ویژگی های مشابه در برنامه های اندروید یا iOS مقایسه شود. APIهای بدون درز برای پر کردن این شکاف تجربه کاربر و توسعهدهنده بین برنامه و وب طراحی شدهاند.
نسخه های نمایشی از pixiv و Tokopedia
پیش نیازها
دانش از:
- HTML
- CSS
- جاوا اسکریپت
- ابزارهای توسعه دهنده گوگل کروم
آنچه یاد خواهید گرفت:
نحوه پیاده سازی:
- پیش اجرا
- bfcache
- پراکسی پیش واکشی خصوصی
- انتقال عناصر ریشه / مشترک
چیزی که خواهی ساخت
یک برنامه وب نمونه ساخته شده با Next.js که با آخرین قابلیت های مرورگر فوری و بدون درز غنی شده است:
- ناوبری تقریباً آنی با پیش اجرا
- bfcache برای بارگذاری فوری با دکمه های عقب و جلو مرورگر
- اولین برداشتهای عالی از ناوبری متقاطع با پراکسی واکشی خصوصی خصوصی یا تبادل امضا شده (SXG)
- انتقال یکپارچه بین صفحات با انتقال عناصر ریشه/اشتراکگذاری شده
آنچه شما نیاز دارید
- کروم نسخه 101 یا بالاتر
2. شروع کنید
پرچمهای کروم را فعال کنید
- به about://flags بروید و پرچمهای
Prerender2
وdocumentTransition API
زمان اجرا را فعال کنید. - مرورگر خود را مجددا راه اندازی کنید.
کد را دریافت کنید
- کد را از این مخزن GitHub در محیط توسعه مورد علاقه خود باز کنید:
git clone -b codelab git@github.com:googlechromelabs/instant-seamless-demo.git
- وابستگی های مورد نیاز برای اجرای سرور را نصب کنید:
npm install
- سرور را روی پورت 3000 راه اندازی کنید:
npm run dev
- در مرورگر خود به http://localhost:3000 بروید.
اکنون می توانید برنامه خود را ویرایش و بهبود دهید. هر زمان که تغییراتی ایجاد می کنید، برنامه مجدداً بارگیری می شود و تغییرات شما مستقیماً قابل مشاهده است.
3. ادغام پیش اجرا
برای هدف این دمو، زمان بارگذاری صفحه جزئیات سبزیجات در برنامه نمونه به دلیل تاخیر دلخواه در سمت سرور بسیار کند است. شما این زمان انتظار را با پیش اجرا حذف می کنید.
برای افزودن دکمههای پیشاجرا به صفحه فهرست سبزیجات و اجازه دادن به آنها پس از کلیک کاربر، اجرای پیشپرداخت را آغاز کنند:
- یک جزء دکمه ایجاد کنید، که تگ اسکریپت 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 ایجاد کنید.
مؤلفه SpeculationRules
به صورت پویا یک تگ اسکریپت را در زمانی که برنامه حالت 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])
}
- کامپوننت ها را با برنامه ادغام کنید.
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 کلیک کنید.
اکنون می توانید بهبود قابل توجه بارگذاری را مشاهده کنید. در حالت استفاده واقعی، پیشاجرا برای صفحهای که کاربر احتمالاً بعداً از آن بازدید میکند، توسط برخی اکتشافیها آغاز میشود.
تجزیه و تحلیل
بهطور پیشفرض، فایل 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()
})
...
عالی است، شما با موفقیت تجزیه و تحلیل خود را طوری تغییر دادید که با پیش اجرا سازگار باشد. اکنون می توانید گزارش های مشاهده صفحه را با زمان بندی مناسب در کنسول مرورگر مشاهده کنید.
4. مسدود کننده های bfcache را حذف کنید
کنترل کننده رویداد unload
بردارید
داشتن یک رویداد unload
غیر ضروری یک اشتباه بسیار رایج است که دیگر توصیه نمی شود. نه تنها از کار کردن bfcache جلوگیری می کند، بلکه قابل اعتماد نیست. برای مثال، همیشه در موبایل و سافاری شلیک نمیشود.
به جای یک رویداد unload
، از رویداد pagehide
استفاده میکنید که در تمام مواردی که رویداد unload
فعال میشود و زمانی که یک صفحه در bfcache قرار میگیرد، فعال میشود.
برای حذف کنترل کننده رویداد unload
:
- در فایل
analytics.js
، کد مربوط بهunload
event handler را با کدpagehide
event handler جایگزین کنید:
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: no-store
از ویژگی bfcache مرورگر بهره نمیبرند، بنابراین تمرین خوبی است که در این هدر صرفهجویی کنید. به طور خاص، اگر صفحه حاوی اطلاعات شخصی یا حیاتی نیست، مانند حالت ورود به سیستم، احتمالاً نیازی به ارائه آن با هدر HTTP Cache-control: no-store
ندارید.
برای به روز رسانی هدر کنترل حافظه پنهان برنامه نمونه:
- کد
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')
...
تعیین کنید که آیا یک صفحه از bfcache بازیابی شده است یا خیر
رویداد pageshow
بلافاصله پس از load
، زمانی که صفحه در ابتدا بارگیری می شود و هر زمانی که صفحه از bfcache بازیابی می شود، فعال می شود. رویداد pageshow
دارای یک ویژگی persisted
است که اگر صفحه از bfcache بازیابی شده باشد درست است و اگر اینطور نبود false است. میتوانید از ویژگی persisted
برای تشخیص بارگذاریهای صفحه معمولی از بازیابیهای bfcache استفاده کنید. سرویس های اصلی تجزیه و تحلیل باید از 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 Developer Tools میتواند به شما کمک کند صفحات خود را آزمایش کنید تا مطمئن شوید که برای bfcache بهینه شدهاند و هر مشکلی را که ممکن است آنها را واجد شرایط نباشند شناسایی کنید.
برای تست یک صفحه خاص:
- به صفحه کروم بروید.
- در Chrome Developer Tools، روی Application > Back-Forward Cache > Run Test کلیک کنید.
Chrome Developer Tools تلاش میکند به دور و سپس به عقب برگردد تا تعیین کند آیا صفحه را میتوان از bfcache بازیابی کرد یا خیر.
در صورت موفقیت آمیز بودن، پانل به شما می گوید که صفحه از کش عقب بازیابی شده است:
اگر ناموفق بود، پانل به شما می گوید که صفحه بازیابی نشده است و دلیل آن. اگر دلیل چیزی است که می توانید به عنوان یک توسعه دهنده به آن بپردازید، پانل نیز به شما این را می گوید.
5. واکشی اولیه بین سایتی را فعال کنید
واکشی اولیه واکشی زودتر شروع می شود به طوری که بایت ها از قبل در مرورگر زمانی که کاربر پیمایش می کند، وجود دارد که ناوبری را تسریع می کند. این یک راه آسان برای بهبود Core Web Vitals و جبران برخی از فعالیت های شبکه قبل از ناوبری است. این امر مستقیماً بزرگترین رنگ محتوایی (LCP) را تسریع میکند و فضای بیشتری را برای تأخیر ورودی اول (FID) و تغییر چیدمان تجمعی (CLS) پس از پیمایش میدهد.
پراکسی Prefetch خصوصی واکشی اولیه بین سایتی را فعال می کند، اما اطلاعات خصوصی کاربر را به سرور مقصد فاش نمی کند.
واکشی اولیه بین سایتی را با پراکسی Prefetch خصوصی فعال کنید
صاحبان وبسایتها کنترل واکشی اولیه را از طریق یک منبع مشاوره ترافیکی معروف، مشابه /robots.txt
برای خزندههای وب، حفظ میکنند، که به سرور HTTP اجازه میدهد تا اعلام کند که عوامل پیادهسازی باید توصیههای مربوطه را اعمال کنند. در حال حاضر، صاحبان وبسایتها میتوانند به نماینده توصیه کنند که اتصالات شبکه را غیرفعال یا دریچه گاز باشد. در آینده، توصیه های دیگری ممکن است اضافه شود.
برای میزبانی یک منبع راهنمایی ترافیک:
- این فایل JSON مانند را اضافه کنید:
عمومی/.معروف/توصیه-ترافیک
[
{
"user_agent": "prefetch-proxy",
"google_prefetch_proxy_eap": {
"fraction": 1
}
}
]
فیلد google_prefetch_proxy_eap
یک فیلد ویژه برای برنامه دسترسی اولیه است و فیلد fraction
فیلدی برای کنترل کسری از واکشی های اولیه درخواستی است که پراکسی Prefetch خصوصی ارسال می کند.
توصیه های ترافیکی باید با نوع 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
6. یکپارچه سازی 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()
را فراخوانی کنید تا تابع callbacktransition.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 آن را جذابتر کنید.
7. تبریک می گویم
تبریک می گویم! شما یک برنامه وب فوری و بدون درز با تجربه کاربری کم اصطکاک، جذاب و بصری ایجاد کردید.
بیشتر بدانید
پیش اجرا
bfcache
پیش واکشی بین سایتی
مبادلات امضا شده
انتقال عناصر ریشه / مشترک
این APIها هنوز در مراحل اولیه توسعه هستند، بنابراین لطفاً نظرات خود را در crbug.com یا به عنوان مشکل در مخزن Github APIهای مربوطه به اشتراک بگذارید.