เพิ่มการนำทางแบบทันทีและการเปลี่ยนหน้าอย่างราบรื่นไปยังเว็บแอป

1. ก่อนเริ่มต้น

Codelab นี้จะสอนวิธีเพิ่มการนำทางแบบทันทีและการเปลี่ยนหน้าอย่างราบรื่นไปยังเว็บแอปตัวอย่างด้วย API ล่าสุดที่ Google Chrome สนับสนุนตั้งแต่แรก

เว็บแอปตัวอย่างจะตรวจสอบคุณค่าทางโภชนาการของผักและผลไม้ยอดนิยม หน้ารายการผลไม้และหน้ารายละเอียดผลไม้สร้างขึ้นเป็นแอปหน้าเดียว (SPA) และหน้ารายการผักและรายละเอียดผักสร้างขึ้นเป็นแอปหลายหน้า (MPA) แบบดั้งเดิม

ตัวอย่างภาพหน้าจอของแอปในอุปกรณ์เคลื่อนที่ ตัวอย่างภาพหน้าจอของแอปในอุปกรณ์เคลื่อนที่

กล่าวอย่างเจาะจงคือ คุณควรใช้การแสดงผลล่วงหน้า, แคชย้อนหลัง (bfcache) และพร็อกซีการดึงข้อมูลล่วงหน้าส่วนตัวสำหรับการไปยังส่วนต่างๆ แบบทันที และใช้การเปลี่ยนองค์ประกอบระดับรูท/ที่แชร์เพื่อให้การเปลี่ยนหน้าเป็นไปอย่างราบรื่น คุณใช้การแสดงผลล่วงหน้าและ bfcache สำหรับหน้า MPA และการเปลี่ยนองค์ประกอบที่แชร์สำหรับหน้า SPA

ความเร็วเว็บไซต์เป็นแง่มุมที่สำคัญของประสบการณ์ของผู้ใช้เสมอ ด้วยเหตุนี้ Google จึงเปิดตัว Core Web Vitals ซึ่งเป็นชุดเมตริกที่วัดประสิทธิภาพการโหลด การโต้ตอบ และความเสถียรของภาพในหน้าเว็บเพื่อวัดประสบการณ์จริงของผู้ใช้ API ล่าสุดช่วยให้คุณเพิ่มคะแนน Core Web Vitals ของเว็บไซต์ในภาคสนาม โดยเฉพาะในส่วนของประสิทธิภาพในการโหลด

รูปภาพสาธิตวิธีที่ bfcache ปรับปรุงเวลาที่ใช้ในการโหลด

การสาธิตจาก Mindvalley

นอกจากนี้ ผู้ใช้ยังคุ้นเคยกับการใช้งานทรานซิชันเพื่อทำให้การนำทางและการเปลี่ยนแปลงสถานะใช้งานได้ง่ายมากในแอปที่มาพร้อมเครื่องบนอุปกรณ์เคลื่อนที่ แต่น่าเสียดายที่การจำลองประสบการณ์ของผู้ใช้ดังกล่าวไม่ซับซ้อนบนเว็บ แม้ว่าการใช้ API ของแพลตฟอร์มบนเว็บในปัจจุบันอาจให้ผลลัพธ์ที่คล้ายกัน แต่การพัฒนาอาจยากหรือซับซ้อนเกินไป โดยเฉพาะเมื่อเทียบกับรุ่นอื่นๆ ของฟีเจอร์ในแอป Android หรือ iOS API ที่ราบรื่นออกแบบมาเพื่อเติมเต็มช่องว่างระหว่างประสบการณ์ของผู้ใช้และนักพัฒนาแอปนี้ระหว่างแอปและเว็บ

การสาธิต Shared Element Transitions API จาก pixiv การสาธิต Element Transitions API ที่แชร์จาก Tokopedia

การสาธิตจาก pixiv และ Tokopedia

ข้อกำหนดเบื้องต้น

ความรู้เกี่ยวกับ:

สิ่งที่คุณจะได้เรียนรู้มีดังนี้

วิธีการใช้

  • การแสดงผลล่วงหน้า
  • bfcache
  • พร็อกซีการดึงข้อมูลล่วงหน้าส่วนตัว
  • การเปลี่ยนองค์ประกอบรูท/ที่แชร์

สิ่งที่คุณจะสร้าง

ตัวอย่างเว็บแอปที่สร้างด้วย Next.js ซึ่งแต่งเติมด้วยความสามารถของเบราว์เซอร์ที่รวดเร็วและราบรื่นล่าสุด:

  • การนำทางที่แทบจะในทันทีด้วยการแสดงผลล่วงหน้า
  • bfcache สำหรับการโหลดทันทีด้วยปุ่มย้อนกลับและไปข้างหน้าของเบราว์เซอร์
  • การแสดงผลครั้งแรกที่ยอดเยี่ยมจากการนำทางแบบข้ามต้นทางด้วยพร็อกซีการดึงข้อมูลล่วงหน้าส่วนตัวหรือ Signed Exchange (SXG)
  • การเปลี่ยนหน้าอย่างราบรื่นระหว่างหน้าต่างๆ ด้วยการเปลี่ยนองค์ประกอบรูท/แชร์

สิ่งที่ต้องมี

  • Chrome เวอร์ชัน 101 ขึ้นไป

2. เริ่มต้นใช้งาน

เปิดใช้ Chrome Flag

  1. ไปที่ about://flags แล้วเปิดใช้แฟล็กรันไทม์ Prerender2 และ documentTransition API
  2. รีสตาร์ทเบราว์เซอร์

รับโค้ด

  1. เปิดโค้ดจากที่เก็บ GitHub นี้ในสภาพแวดล้อมการพัฒนาที่คุณชื่นชอบ
git clone -b codelab git@github.com:googlechromelabs/instant-seamless-demo.git
  1. ติดตั้งการอ้างอิงที่จำเป็นสำหรับการเรียกใช้เซิร์ฟเวอร์ ดังนี้
npm install
  1. เริ่มต้นเซิร์ฟเวอร์บนพอร์ต 3000 ด้วยคำสั่งต่อไปนี้
npm run dev
  1. ไปที่ http://localhost:3000 ในเบราว์เซอร์

ตอนนี้คุณแก้ไขและปรับปรุงแอปได้แล้ว เมื่อคุณทำการเปลี่ยนแปลง แอปจะโหลดซ้ำและคุณจะเห็นการเปลี่ยนแปลงโดยตรง

3. ผสานรวมการแสดงผลล่วงหน้า

สำหรับการสาธิตนี้ เวลาในการโหลดหน้ารายละเอียดผักในแอปตัวอย่างนั้นช้ามากเนื่องจากความล่าช้าในฝั่งเซิร์ฟเวอร์ โดยไม่ต้องรอนานด้วยการแสดงผลล่วงหน้า

วิธีเพิ่มปุ่มการแสดงผลล่วงหน้าลงในหน้ารายการผักและให้ปุ่มทริกเกอร์การแสดงผลล่วงหน้าหลังจากที่ผู้ใช้คลิก

  1. สร้างคอมโพเนนต์ปุ่มที่จะแทรกแท็กสคริปต์กฎการคาดเดาแบบไดนามิก ดังนี้

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>
  )
}
  1. นำเข้าคอมโพเนนต์ 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>
  )
}
  1. สร้างคอมโพเนนต์เพื่อเพิ่ม 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])
}
  1. ผสานรวมคอมโพเนนต์กับแอป

หน้า/_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
  1. คลิกแสดงผลล่วงหน้า

ตอนนี้คุณจะเห็นการปรับปรุงที่สำคัญในการโหลด ในการใช้งานจริง ระบบจะทริกเกอร์การแสดงผลล่วงหน้าสำหรับหน้าเว็บที่ผู้ใช้มีแนวโน้มที่จะเข้าชมเป็นลำดับถัดไปตามการเรียนรู้บางอย่าง

วิดีโอตัวอย่างแอปสาธิตสำหรับการแสดงผลล่วงหน้า

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()
  })
  ...

เยี่ยมมาก คุณแก้ไขข้อมูลวิเคราะห์ให้เข้ากันได้กับการแสดงผลล่วงหน้าเรียบร้อยแล้ว ตอนนี้คุณสามารถดูบันทึกการดูหน้าเว็บในช่วงเวลาที่เหมาะสมได้ในคอนโซลเบราว์เซอร์

4. นำตัวบล็อก bfcache ออก

นำเครื่องจัดการเหตุการณ์ unload ออก

การมีเหตุการณ์ unload ที่ไม่จำเป็นเป็นข้อผิดพลาดที่พบได้บ่อยมากซึ่งไม่แนะนำอีกต่อไป ซึ่งไม่เพียงป้องกันไม่ให้ bfcache ทำงานได้ แต่ยังไม่น่าเชื่อถืออีกด้วย ตัวอย่างเช่น เบราว์เซอร์จะไม่เริ่มทำงานบนอุปกรณ์เคลื่อนที่และ Safari เสมอไป

คุณจะใช้เหตุการณ์ pagehide แทนเหตุการณ์ unload ซึ่งจะเริ่มทํางานในทุกกรณีที่เหตุการณ์ unload เริ่มทำงานและเมื่อหน้าเว็บวางใน bfcache

วิธีนําเครื่องจัดการเหตุการณ์ 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 จะไม่ได้รับประโยชน์จากฟีเจอร์ bfcache ของเบราว์เซอร์ คุณจึงควรใช้ส่วนหัวนี้อย่างประหยัด โดยเฉพาะอย่างยิ่ง หากหน้าเว็บไม่มีข้อมูลที่ปรับเปลี่ยนในแบบของคุณหรือข้อมูลสำคัญ เช่น สถานะที่ลงชื่อเข้าใช้ คุณอาจไม่จำเป็นต้องแสดงหน้าดังกล่าวด้วยส่วนหัว HTTP ของ Cache-control: no-store

วิธีอัปเดตส่วนหัวการควบคุมแคชของแอปตัวอย่าง

  • แก้ไขโค้ด getServerSideProps

หน้า/ผัก/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')
  ...

page/vegetables/[ชื่อ].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 สามารถช่วยคุณทดสอบหน้าเว็บเพื่อให้มั่นใจว่าหน้าเว็บได้รับการเพิ่มประสิทธิภาพสำหรับ bfcache และสามารถระบุปัญหาที่อาจทำให้หน้าเว็บไม่มีสิทธิ์

หากต้องการทดสอบหน้าใดหน้าหนึ่ง ให้ทำดังนี้

  1. ไปที่หน้าเว็บใน Chrome
  2. ในเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ Chrome ให้คลิกแอปพลิเคชัน > Back-Forward Cache > ดำเนินการทดสอบ

เครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ Chrome จะพยายามออกจากหน้าแล้วย้อนกลับมาเพื่อพิจารณาว่าจะคืนค่าหน้าเว็บจาก bfcache ได้ไหม

49bf965af35d5324.png

หากสำเร็จ แผงจะบอกคุณว่ามีการคืนค่าหน้าจาก Back-Forward Cache ดังนี้

47015a0de45f0b0f.png

หากไม่สำเร็จ แผงจะแจ้งให้คุณทราบว่าระบบไม่ได้คืนค่าหน้าเว็บพร้อมทั้งอธิบายสาเหตุ หากเหตุผลคือสิ่งที่คุณแก้ไขในฐานะนักพัฒนาซอฟต์แวร์ได้ แผงข้อมูลนี้จะบอกคุณด้วย

dcf0312c3fc378ce.png

5. เปิดใช้การดึงข้อมูลล่วงหน้าข้ามเว็บไซต์

การดึงข้อมูลล่วงหน้าจะเริ่มดึงข้อมูลก่อนเวลาเพื่อให้ผู้ใช้มีข้อมูลไบต์อยู่ในเบราว์เซอร์แล้ว ซึ่งจะช่วยเร่งการนำทาง ซึ่งเป็นวิธีง่ายๆ ในการปรับปรุง Core Web Vitals และชดเชยกิจกรรมบางอย่างในเครือข่ายก่อนการนำทาง ซึ่งจะเร่ง Largest Contentful Paint (LCP) โดยตรง และเพิ่มพื้นที่สำหรับ First Input Delay (FID) และ Cumulative Layout Shift (CLS) ขณะไปยังส่วนต่างๆ

พร็อกซีการดึงข้อมูลล่วงหน้าส่วนตัวจะเปิดใช้การดึงข้อมูลล่วงหน้าข้ามเว็บไซต์ แต่จะไม่เปิดเผยข้อมูลส่วนตัวเกี่ยวกับผู้ใช้ไปยังเซิร์ฟเวอร์ปลายทาง

วิธีการทำงานของพร็อกซีการดึงข้อมูลล่วงหน้าแบบส่วนตัว

เปิดใช้การดึงข้อมูลล่วงหน้าข้ามเว็บไซต์ด้วยพร็อกซีการดึงข้อมูลล่วงหน้าส่วนตัว

เจ้าของเว็บไซต์ยังคงควบคุมการดึงข้อมูลล่วงหน้าผ่านทรัพยากรคําแนะนําการเข้าชมที่รู้จักกันดี ซึ่งคล้ายกับ /robots.txt สําหรับโปรแกรมรวบรวมข้อมูลเว็บ ซึ่งช่วยให้เซิร์ฟเวอร์ HTTP ประกาศว่าการใช้ Agent ควรใช้คําแนะนําที่สอดคล้องกัน ในตอนนี้ เจ้าของเว็บไซต์สามารถแนะนําให้ไม่อนุญาตหรือควบคุมการเชื่อมต่อเครือข่ายได้ ในอนาคตอาจมีการเพิ่มคำแนะนำอื่นๆ อีก

หากต้องการโฮสต์แหล่งข้อมูลคำแนะนำเกี่ยวกับการเข้าชม ให้ทำดังนี้

  1. เพิ่มไฟล์ที่มีลักษณะเหมือน JSON นี้:

สาธารณะ/.well-known/traffic-advice

[
  {
    "user_agent": "prefetch-proxy",
    "google_prefetch_proxy_eap": {
      "fraction": 1
    }
  }
]

ช่อง google_prefetch_proxy_eap เป็นช่องพิเศษสำหรับโปรแกรมทดลองใช้ก่อนเปิดตัว และช่อง fraction เป็นช่องสำหรับควบคุมสัดส่วนของการดึงข้อมูลล่วงหน้าที่ขอซึ่งพร็อกซีการดึงข้อมูลล่วงหน้าส่วนตัวส่ง

ควรแสดงผลคำแนะนำด้านการเข้าชมพร้อมกับประเภท MIME application/trafficadvice+json

  1. ในไฟล์ 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. ผสานรวม API การเปลี่ยนองค์ประกอบที่แชร์

เมื่อผู้ใช้ท่องเว็บจากหน้าหนึ่งไปยังอีกหน้าหนึ่ง เนื้อหาที่ผู้ใช้เห็นจะเปลี่ยนไปอย่างกะทันหันโดยไม่คาดคิดเมื่อหน้าแรกหายไปและหน้าเว็บใหม่จะปรากฏขึ้น ประสบการณ์ของผู้ใช้ที่ไม่เชื่อมต่อและต่อเนื่องนี้ก่อให้เกิดความสับสนและส่งผลให้มีระบบสติปัญญาสูงขึ้น นอกจากนี้ ประสบการณ์การใช้งานนี้ยังช่วยเพิ่มจำนวนผู้ใช้ที่รับรู้ถึงการโหลดหน้าเว็บในขณะที่รอให้ปลายทางที่ต้องการโหลดด้วย

ภาพเคลื่อนไหวการโหลดแบบราบรื่นลดภาระด้านการรับรู้เนื่องจากผู้ใช้อยู่ในบริบทขณะไปยังหน้าต่างๆ และลดเวลาในการตอบสนองที่รู้สึกได้ของการโหลดเนื่องจากผู้ใช้เห็นสิ่งที่น่าดึงดูดและน่ารื่นรมย์ในระหว่างนี้ แพลตฟอร์มส่วนใหญ่จึงมีเวอร์ชันพื้นฐานที่ใช้งานง่ายซึ่งช่วยให้นักพัฒนาแอปสร้างการเปลี่ยนผ่านที่ราบรื่นได้ เช่น Android, iOS, MacOS และ Windows

Shared Element Transitions API ช่วยให้นักพัฒนาซอฟต์แวร์มีความสามารถบนเว็บในลักษณะเดียวกัน ไม่ว่าการเปลี่ยนนั้นจะเป็นแบบข้ามเอกสาร (MPA) หรือภายในเอกสาร (SPA)

การสาธิต Shared Element Transitions API จาก pixiv การสาธิต Element Transitions API ที่แชร์จาก Tokopedia

การสาธิตจาก pixiv และ Tokopedia

ในการผสานรวม Shared Element Transitions API กับส่วน SPA ของแอปตัวอย่าง ให้ทำดังนี้

  1. สร้างฮุกที่กำหนดเองเพื่อจัดการการเปลี่ยนในไฟล์ 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
}
  1. เรียกฮุกที่กำหนดเอง usePageTransitionPrep() ในหน้ารายการ แล้วเรียกใช้ฟังก์ชันอะซิงโครนัสเพื่อทริกเกอร์เมธอด 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>
  )
}
  1. ในหน้ารายละเอียด ให้เรียกใช้ฮุก usePageTransition() เพื่อสิ้นสุดฟังก์ชัน Callback ของ transition.start()

ใน Callback นี้ ระบบจะลงทะเบียนองค์ประกอบที่แชร์ในหน้ารายละเอียดด้วย

page/fruits/[ชื่อ].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

การดึงข้อมูลล่วงหน้าข้ามเว็บไซต์

Signed Exchange

การเปลี่ยนองค์ประกอบรูท/ที่แชร์

API เหล่านี้ยังอยู่ในขั้นเริ่มต้นของการพัฒนา ดังนั้นโปรดแชร์ความคิดเห็นของคุณที่ crbug.com หรือเป็นปัญหาในที่เก็บ GitHub ของ API ที่เกี่ยวข้อง