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

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

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

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

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

โดยเฉพาะอย่างยิ่ง คุณใช้ การแสดงผลล่วงหน้า Back-Forward Cache (bfcache) และ Private Prefetch Proxy เพื่อการไปยังส่วนต่างๆ ที่รวดเร็ว และใช้การเปลี่ยนองค์ประกอบราก/ที่แชร์เพื่อการเปลี่ยนหน้าเว็บที่ราบรื่น คุณใช้การแสดงผลล่วงหน้าและ bfcache สำหรับหน้า MPA และการเปลี่ยนองค์ประกอบที่ใช้ร่วมกันสำหรับหน้า SPA

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

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

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

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

การสาธิต Shared Element Transitions API จาก pixiv การสาธิต Shared 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. ผสานรวมการแสดงผลล่วงหน้า

เพื่อจุดประสงค์ของการสาธิตนี้ เวลาที่ใช้ในการโหลดหน้า vegetable-details ในแอปตัวอย่างจึงช้ามากเนื่องจากมีการหน่วงเวลาโดยพลการที่ฝั่งเซิร์ฟเวอร์ คุณจะลดเวลารอนี้ได้ด้วยการแสดงผลล่วงหน้า

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

  1. สร้างคอมโพเนนต์ปุ่มซึ่งจะแทรกแท็กสคริปต์ 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>
  )
}
  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. ผสานรวมคอมโพเนนต์กับแอป

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
  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')
})

อัปเดตส่วนหัว Cache-Control

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

วิธีอัปเดตส่วนหัว 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')
  ...

พิจารณาว่าหน้าเว็บได้รับการคืนค่าจาก bfcache หรือไม่

เหตุการณ์ pageshow จะทริกเกอร์ทันทีหลังจากเหตุการณ์ load เมื่อหน้าเว็บโหลดครั้งแรกและทุกครั้งที่กู้คืนหน้าเว็บจาก bfcache pageshow เหตุการณ์มีพร็อพเพอร์ตี้ persisted ซึ่งเป็นจริงหากมีการกู้คืนหน้าจาก bfcache และเป็นเท็จหากไม่มีการกู้คืน คุณใช้พร็อพเพอร์ตี้ 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 ให้คลิกแอปพลิเคชัน > แคชย้อนกลับ/ไปข้างหน้า > เรียกใช้การทดสอบ

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

49bf965af35d5324.png

หากสำเร็จ แผงจะแจ้งให้คุณทราบว่าระบบกู้คืนหน้าจากแคชย้อนหลังแล้ว

47015a0de45f0b0f.png

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

dcf0312c3fc378ce.png

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

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

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

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

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

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

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

  1. เพิ่มไฟล์ที่มีลักษณะคล้าย 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

  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. ผสานรวม Shared Element Transitions API

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

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

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

การสาธิต Shared Element Transitions API จาก pixiv การสาธิต Shared 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. ในหน้ารายละเอียด ให้เรียกใช้ Hook usePageTransition() เพื่อสิ้นสุดฟังก์ชัน Callback 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 ได้ด้วย

วิดีโอสาธิตแอปตัวอย่างที่ไม่มีการเปลี่ยนฉากองค์ประกอบที่ใช้ร่วมกัน วิดีโอสาธิตแอปตัวอย่างที่มีการเปลี่ยนฉากองค์ประกอบที่ใช้ร่วมกัน

7. ขอแสดงความยินดี

ยินดีด้วย คุณสร้างเว็บแอปที่รวดเร็วและราบรื่นพร้อมประสบการณ์ของผู้ใช้ที่น่าสนใจ ใช้งานง่าย และมีอุปสรรคน้อย

ดูข้อมูลเพิ่มเติม

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

bfcache

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

Signed Exchange

การเปลี่ยนภาพขององค์ประกอบราก/ที่ใช้ร่วมกัน

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