Back-Forward Cache

เพิ่มประสิทธิภาพหน้าเว็บเพื่อให้โหลดได้ทันทีเมื่อใช้ปุ่มย้อนกลับและไปข้างหน้าของเบราว์เซอร์

Back/Forward Cache (หรือ bfcache) คือการเพิ่มประสิทธิภาพเบราว์เซอร์ที่ช่วยให้มีการนำทางกลับและไปข้างหน้าแบบทันที เครื่องมือนี้ช่วยปรับปรุงประสบการณ์การท่องเว็บของผู้ใช้ได้อย่างมาก โดยเฉพาะผู้ที่มีเครือข่ายหรืออุปกรณ์ที่ช้า

ในฐานะนักพัฒนาเว็บ คุณต้องเข้าใจวิธีเพิ่มประสิทธิภาพหน้าเว็บสําหรับ bfcache ในทุกเบราว์เซอร์ เพื่อให้ผู้ใช้ได้รับประโยชน์

ความเข้ากันได้กับเบราว์เซอร์

bfcache รองรับทั้ง Firefox และ Safari มาหลายปีแล้วทั้งในเดสก์ท็อปและอุปกรณ์เคลื่อนที่

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

ข้อมูลเบื้องต้นเกี่ยวกับ bfcache

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

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

ไม่ได้เปิดใช้ bfcache คำขอใหม่จะเริ่มต้นขึ้นเพื่อโหลดหน้าก่อนหน้า และเบราว์เซอร์อาจต้องดาวน์โหลดใหม่ แยกวิเคราะห์ใหม่ และเรียกใช้ทรัพยากรบางส่วน (หรือทั้งหมด) ที่เพิ่งดาวน์โหลดมาใหม่ ขึ้นอยู่กับว่าหน้าเว็บนั้นมีการ เพิ่มประสิทธิภาพการเข้าชมซ้ำได้ดีเพียงใด
พร้อมเปิดใช้ bfcache การโหลดหน้าก่อนหน้าจะเกิดขึ้นทันที เนื่องจากกู้คืนหน้าเว็บทั้งหมดจากหน่วยความจำได้โดยไม่ต้องไปที่เครือข่ายเลย

ลองดูวิดีโอตัวอย่างของ bfcache นี้เพื่อให้เข้าใจถึงความเร็วที่นำไปสู่การไปยังส่วนต่างๆ

ในวิดีโอด้านบน ตัวอย่างที่มี bfcache ค่อนข้างเร็วกว่าตัวอย่างที่ไม่มี bfcache อยู่เล็กน้อย

bfcache ไม่เพียงช่วยให้การนำทางเร็วขึ้นเท่านั้น แต่ยังลดปริมาณการใช้อินเทอร์เน็ตอีกด้วย เพราะไม่ต้องดาวน์โหลดทรัพยากรซ้ำอีก

ข้อมูลการใช้งาน Chrome แสดงให้เห็นว่า 1 ใน 10 การนำทางบนเดสก์ท็อปและ 1 ใน 5 การนำทางบนอุปกรณ์เคลื่อนที่ จะย้อนกลับหรือไปข้างหน้า เมื่อเปิดใช้ bfcache แล้ว เบราว์เซอร์จะกำจัด การโอนข้อมูลและเวลาที่ใช้ในการโหลดหน้าเว็บหลายพันล้านหน้าในแต่ละวัน

วิธีการทำงานของ "แคช"

"แคช" ที่ bfcache ใช้จะแตกต่างจากแคช HTTP (ซึ่งมีประโยชน์ในการเพิ่มความเร็วการนำทางซ้ำด้วย) bfcache เป็นสแนปชอตของทั้งหน้าในหน่วยความจำ (รวมถึงฮีป JavaScript) ขณะที่แคช HTTP มีเฉพาะการตอบกลับสำหรับคำขอที่สร้างไว้ก่อนหน้านี้ เนื่องจากแคช HTTP จะดำเนินการตามคำขอทั้งหมดที่จำเป็นเพื่อโหลดหน้าเว็บค่อนข้างน้อย ดังนั้นการกลับมาเข้าชมซ้ำโดยใช้การคืนค่า bfcache จะรวดเร็วกว่าการไปยังส่วนต่างๆ แบบไม่ใช่ bfcache ที่เพิ่มประสิทธิภาพสูงสุดเสมอ

อย่างไรก็ตาม การสร้างสแนปชอตของหน้าในหน่วยความจำจะมีความซับซ้อนบางประการเนื่องจากต้องเก็บรักษาโค้ดที่อยู่ระหว่างดำเนินการจึงจะดีที่สุด ตัวอย่างเช่น คุณจะจัดการการเรียก setTimeout() เมื่อถึงระยะหมดเวลาขณะที่หน้าเว็บอยู่ใน bfcache ได้อย่างไร

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

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

ดูรายละเอียดเพิ่มเติมเกี่ยวกับวิธีที่การใช้ API ต่างๆ ส่งผลต่อการมีสิทธิ์ bfcache ของหน้าเว็บได้ที่เพิ่มประสิทธิภาพหน้าเว็บสำหรับ bfcache ด้านล่าง

แอป bfcache และแอปหน้าเว็บเดียว (SPA)

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

API สำหรับสังเกต bfcache

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

เหตุการณ์หลักที่ใช้สังเกต bfcache คือเหตุการณ์การเปลี่ยนหน้าเว็บ pageshow และ pagehide ซึ่งมีมานานเท่าที่ bfcache มีและรองรับในเบราว์เซอร์ส่วนใหญ่ที่ใช้อยู่

เหตุการณ์วงจร หน้าเว็บที่ใหม่กว่าคือ freeze และ resume จะถูกส่งเมื่อหน้าเว็บเข้าหรือออกจาก bfcache รวมถึงในสถานการณ์อื่นๆ ด้วย เช่น เมื่อแท็บพื้นหลังค้างเพื่อลดการใช้ CPU โปรดทราบว่าปัจจุบันเหตุการณ์ในวงจรของหน้าเว็บ รองรับเฉพาะในเบราว์เซอร์แบบ Chromium เท่านั้น

สังเกตเมื่อมีการคืนค่าหน้าจาก bfcache

เหตุการณ์ pageshow จะเริ่มทำงานหลังจากเหตุการณ์ load เมื่อหน้าเว็บกำลังโหลดเป็นครั้งแรกและทุกครั้งที่มีการคืนค่าหน้าจาก bfcache เหตุการณ์ pageshow มีพร็อพเพอร์ตี้ persisted ซึ่งจะมีค่าเป็น true หากหน้าเว็บได้รับการกู้คืนจาก bfcache (และ false หากไม่) คุณใช้พร็อพเพอร์ตี้ persisted เพื่อแยกการโหลดหน้าเว็บปกติออกจากการคืนค่า bfcache ได้ เช่น

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    console.log('This page was restored from the bfcache.');
  } else {
    console.log('This page was loaded normally.');
  }
});

ในเบราว์เซอร์ที่รองรับ Page Lifecycle API เหตุการณ์ resume จะเริ่มทำงานด้วยเมื่อมีการคืนค่าหน้าเว็บจาก bfcache (ก่อนเหตุการณ์ pageshow ทันที) แต่จะเริ่มทำงานเมื่อผู้ใช้ไปที่แท็บพื้นหลังที่ค้างอีกครั้งด้วย หากต้องการอัปเดตสถานะของหน้าเว็บหลังจากที่หยุดค้าง (ซึ่งรวมถึงหน้าใน bfcache) ให้ใช้เหตุการณ์ resume แต่หากต้องการวัดอัตรา Hit ของ bfcache ของเว็บไซต์ คุณจะต้องใช้เหตุการณ์ pageshow ในบางกรณี คุณอาจต้องใช้ทั้ง 2 อย่าง

สังเกตเมื่อหน้าเว็บป้อน bfcache

เหตุการณ์ pagehide เป็นเหตุการณ์ pageshow ซ้ำ เหตุการณ์ pageshow จะเริ่มทำงานเมื่อหน้าเว็บโหลดตามปกติหรือคืนค่าจาก bfcache เหตุการณ์ pagehide จะเริ่มทำงานเมื่อหน้าเว็บยกเลิกการโหลดตามปกติหรือเมื่อเบราว์เซอร์พยายามใส่หน้านั้นลงใน bfcache

เหตุการณ์ pagehide ยังมีพร็อพเพอร์ตี้ persisted ด้วย และหากเป็น false ก็มั่นใจได้ว่าหน้าเว็บไม่ได้กำลังป้อน bfcache แต่หากพร็อพเพอร์ตี้ persisted คือ true ก็ไม่ได้เป็นการรับประกันว่าจะมีการแคชหน้าเว็บ ซึ่งหมายความว่าเบราว์เซอร์intendsให้แคชหน้าเว็บไว้ แต่อาจมีปัจจัยที่ทำให้แคชไม่ได้

window.addEventListener('pagehide', (event) => {
  if (event.persisted) {
    console.log('This page *might* be entering the bfcache.');
  } else {
    console.log('This page will unload normally and be discarded.');
  }
});

ในทํานองเดียวกัน เหตุการณ์ freeze จะเริ่มทํางานทันทีหลังจากเหตุการณ์ pagehide (หากพร็อพเพอร์ตี้ persisted ของเหตุการณ์คือ true) แต่หมายความว่าเบราว์เซอร์intendsให้แคชหน้าเว็บไว้เช่นกัน แต่อาจยังคงต้องทิ้งโดเมนดังกล่าวด้วยเหตุผลหลายประการตามที่อธิบายไว้ด้านล่างนี้

เพิ่มประสิทธิภาพหน้าเว็บสำหรับ bfcache

หน้าเว็บบางหน้าไม่ได้ถูกจัดเก็บไว้ใน bfcache และแม้จะมีการเก็บหน้าเว็บหนึ่งไว้ก็ตาม หน้าเว็บจะไม่อยู่ในนั้นตลอดไป นักพัฒนาซอฟต์แวร์จำเป็นต้องเข้าใจว่าอะไรที่ทำให้หน้าเว็บมีสิทธิ์ (และไม่มีสิทธิ์) สำหรับ bfcache เพื่อเพิ่มอัตรา Hit สูงสุด

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

ไม่ใช้กิจกรรม unload

วิธีที่สำคัญที่สุดในการเพิ่มประสิทธิภาพสำหรับ bfcache ในทุกเบราว์เซอร์คืออย่าใช้เหตุการณ์ unload เป็นอันขาด เคยไหม

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

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

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

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

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

อันที่จริง Lighthouse มีการตรวจสอบ no-unload-listeners ซึ่งจะเตือนนักพัฒนาซอฟต์แวร์หาก JavaScript ในหน้าเว็บของตน (รวมถึง JavaScript จากไลบรารีของบุคคลที่สาม) เพิ่ม Listener เหตุการณ์ unload

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

ใช้นโยบายสิทธิ์เพื่อป้องกันไม่ให้มีการใช้ตัวแฮนเดิลการยกเลิกการโหลดในหน้าเว็บ

เว็บไซต์ที่ไม่ได้ใช้เครื่องจัดการเหตุการณ์ unload จะตรวจสอบให้แน่ใจว่าไม่ได้เพิ่มเครื่องจัดการเหตุการณ์เหล่านี้โดยใช้นโยบายสิทธิ์จาก Chrome 115

Permission-Policy: unload()

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

เพิ่ม Listener beforeunload อย่างมีเงื่อนไข

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

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

ไม่ควรทำ
window.addEventListener('beforeunload', (event) => {
  if (pageHasUnsavedChanges()) {
    event.preventDefault();
    return event.returnValue = 'Are you sure you want to exit?';
  }
});
โค้ดด้านบนจะเพิ่ม Listener beforeunload อย่างไม่มีเงื่อนไข
ควรทำ
function beforeUnloadListener(event) {
  event.preventDefault();
  return event.returnValue = 'Are you sure you want to exit?';
};

// A function that invokes a callback when the page has unsaved changes.
onPageHasUnsavedChanges(() => {
  window.addEventListener('beforeunload', beforeUnloadListener);
});

// A function that invokes a callback when the page's unsaved changes are resolved.
onAllChangesSaved(() => {
  window.removeEventListener('beforeunload', beforeUnloadListener);
});
โค้ดด้านบนจะเพิ่ม Listener beforeunload เมื่อจำเป็นเท่านั้น (และนำออกเมื่อไม่จำเป็น)

ลดการใช้ Cache-Control: no-store

Cache-Control: no-store เป็นเว็บเซิร์ฟเวอร์ส่วนหัว HTTP ที่ตั้งค่าการตอบกลับที่สั่งเบราว์เซอร์ไม่ให้จัดเก็บการตอบกลับไว้ในแคช HTTP ใดๆ ได้ ควรใช้สำหรับทรัพยากรที่มีข้อมูลผู้ใช้ที่ละเอียดอ่อน เช่น หน้าเว็บที่อยู่หลังการเข้าสู่ระบบ

แม้ว่า bfcache จะไม่ใช่แคช HTTP แต่ที่ผ่านมา เมื่อมีการตั้งค่า Cache-Control: no-store ในตัวทรัพยากรของหน้าเว็บเอง (ตรงข้ามกับทรัพยากรย่อยใดๆ) เบราว์เซอร์ได้เลือกที่จะไม่จัดเก็บหน้าเว็บไว้ใน bfcache เราได้ดำเนินการเปลี่ยนแปลงลักษณะการทำงานนี้สำหรับ Chrome ในลักษณะที่รักษาความเป็นส่วนตัว แต่ ณ ปัจจุบัน หน้าเว็บใดก็ตามที่ใช้ Cache-Control: no-store จะไม่มีสิทธิ์ใช้ bfcache

เนื่องจาก Cache-Control: no-store จำกัดการมีสิทธิ์ในการใช้ bfcache ของหน้าเว็บ จึงควรตั้งค่าในหน้าที่มีข้อมูลที่ละเอียดอ่อนซึ่งการแคชประเภทที่ไม่เหมาะสมเท่านั้น

สำหรับหน้าเว็บที่ต้องการแสดงเนื้อหาล่าสุดเสมอและเนื้อหานั้นไม่มีข้อมูลที่ละเอียดอ่อน ให้ใช้ Cache-Control: no-cache หรือ Cache-Control: max-age=0 คำสั่งเหล่านี้จะสั่งให้เบราว์เซอร์ตรวจสอบเนื้อหาอีกครั้งก่อนที่จะแสดง และจะไม่มีผลต่อการมีสิทธิ์ bfcache ของหน้าเว็บ

โปรดทราบว่าเมื่อกู้คืนหน้าจาก bfcache แล้ว หน้าจะมีการคืนค่าจากหน่วยความจำ ไม่ใช่จากแคช HTTP ผลที่ได้คือ จะไม่มีการพิจารณาคำสั่ง เช่น Cache-Control: no-cache หรือ Cache-Control: max-age=0 และจะไม่ได้รับการตรวจสอบอีกครั้งก่อนที่เนื้อหาจะแสดงให้ผู้ใช้เห็น

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

อัปเดตข้อมูลเก่าหรือข้อมูลที่ละเอียดอ่อนหลังจากคืนค่า bfcache

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

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

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

เพื่อหลีกเลี่ยงสถานการณ์เช่นนี้ คุณควรอัปเดตหน้าเว็บหลังจากเหตุการณ์ pageshow เสมอหาก event.persisted true

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    // Do any checks and updates to the page
  }
});

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

window.addEventListener('pageshow', (event) => {
  if (event.persisted && !document.cookie.match(/my-cookie/)) {
    // Force a reload if the user has logged out.
    location.reload();
  }
});

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

การคืนค่าโฆษณาและ bfcache

การหลีกเลี่ยงการใช้ bfcache แทนการแสดงโฆษณาชุดใหม่บนการนำทางย้อนหลังแต่ละครั้งอาจกระตุ้นให้อยากทำเช่นนั้น แต่นอกจากจะมีผลกระทบต่อประสิทธิภาพแล้ว ยังมีข้อสงสัยว่าพฤติกรรมดังกล่าวจะก่อให้เกิดการมีส่วนร่วมกับโฆษณาที่ดีขึ้นหรือไม่ ผู้ใช้อาจสังเกตเห็นโฆษณาที่ตนตั้งใจจะกลับมาคลิกแต่เป็นการโหลดซ้ำแทนที่จะกู้คืนจาก bfcache ที่ผู้ใช้ทำไม่ได้ การทดสอบสถานการณ์นี้ (ควรทดสอบด้วยการทดสอบ A/B) เป็นสิ่งสำคัญก่อนที่จะตั้งสมมติฐาน

สําหรับเว็บไซต์ที่ต้องการรีเฟรชโฆษณาในการกู้คืน bfcache จากนั้นรีเฟรชเฉพาะโฆษณาในเหตุการณ์ pageshow เมื่อ event.persisted true ทําให้เหตุการณ์เช่นนี้เกิดขึ้นได้โดยไม่ส่งผลกระทบต่อประสิทธิภาพของหน้าเว็บ ตรวจสอบกับผู้ให้บริการโฆษณา แต่ต่อไปนี้เป็นตัวอย่างหนึ่งเกี่ยวกับวิธีดําเนินการด้วยแท็กการเผยแพร่ของ Google

หลีกเลี่ยงการอ้างอิง window.opener

ในเบราว์เซอร์รุ่นเก่า หากหน้าเว็บเปิดโดยใช้ window.open() จากลิงก์ target=_blank โดยไม่ต้องระบุ rel="noopener" หน้าที่เปิดอยู่จะมีการอ้างอิงไปยังออบเจ็กต์หน้าต่างของหน้าที่เปิดอยู่

นอกเหนือจากความเสี่ยงด้านความปลอดภัยแล้ว คุณจะไม่สามารถใส่หน้าที่มีการอ้างอิง window.opener ที่ไม่เป็นค่าว่างลงใน bfcache ได้อย่างปลอดภัย เนื่องจากอาจทำให้หน้าใดก็ตามที่พยายามเข้าถึงหน้านั้นเสียหาย

ด้วยเหตุนี้ คุณจึงควรหลีกเลี่ยงการสร้างข้อมูลอ้างอิง window.opener คุณสามารถดำเนินการนี้โดยใช้ rel="noopener" เมื่อเป็นไปได้ (โปรดทราบว่าขณะนี้เป็นค่าเริ่มต้นในเบราว์เซอร์สมัยใหม่ทั้งหมด) หากเว็บไซต์กำหนดให้เปิดหน้าต่างและควบคุมการทำงานผ่าน window.postMessage() หรืออ้างอิงออบเจ็กต์หน้าต่างโดยตรง ทั้งหน้าต่างที่เปิดอยู่และที่เปิดจะไม่มีสิทธิ์ใช้ bfcache

ปิดการเชื่อมต่อที่เปิดอยู่ก่อนที่ผู้ใช้จะออกจากหน้าทุกครั้ง

ดังที่กล่าวไว้ข้างต้น เมื่อใส่หน้าเว็บลงใน bfcache งาน JavaScript ที่กำหนดเวลาไว้ทั้งหมดจะหยุดชั่วคราวและกลับมาทำงานอีกครั้งเมื่อหน้าเว็บถูกนำออกจากแคช

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

อย่างไรก็ตาม หากงานเหล่านี้เชื่อมต่อกับ API ที่เข้าถึงได้จากหน้าอื่นๆ ในต้นทางเดียวกัน (เช่น IndexedDB, Web Locks, WebSockets ฯลฯ) ก็อาจเป็นปัญหาเพราะการหยุดงานเหล่านี้ชั่วคราวอาจทำให้โค้ดในแท็บอื่นๆ ไม่ทำงาน

ดังนั้น บางเบราว์เซอร์จะไม่พยายามใส่หน้าใน bfcache ในกรณีต่อไปนี้

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

จากนั้นหากมีการกู้คืนหน้าจาก bfcache คุณจะสามารถเปิดหรือเชื่อมต่อกับ API เหล่านั้นอีกครั้ง (ในเหตุการณ์ pageshow หรือ resume)

ตัวอย่างต่อไปนี้แสดงวิธีตรวจสอบให้แน่ใจว่าหน้าเว็บมีสิทธิ์ใช้ bfcache เมื่อใช้ IndexedDB ด้วยการปิดการเชื่อมต่อแบบเปิดในตัวฟังเหตุการณ์ pagehide

let dbPromise;
function openDB() {
  if (!dbPromise) {
    dbPromise = new Promise((resolve, reject) => {
      const req = indexedDB.open('my-db', 1);
      req.onupgradeneeded = () => req.result.createObjectStore('keyval');
      req.onerror = () => reject(req.error);
      req.onsuccess = () => resolve(req.result);
    });
  }
  return dbPromise;
}

// Close the connection to the database when the user is leaving.
window.addEventListener('pagehide', () => {
  if (dbPromise) {
    dbPromise.then(db => db.close());
    dbPromise = null;
  }
});

// Open the connection when the page is loaded or restored from bfcache.
window.addEventListener('pageshow', () => openDB());

ทดสอบว่าหน้าเว็บของคุณสามารถแคชได้

เครื่องมือสำหรับนักพัฒนาเว็บใน Chrome ช่วยคุณทดสอบหน้าเว็บเพื่อให้มั่นใจว่าหน้านั้นๆ ได้รับการเพิ่มประสิทธิภาพสำหรับ bfcache แล้ว รวมถึงระบุปัญหาที่อาจทำให้หน้าเหล่านั้นไม่มีสิทธิ์

หากต้องการทดสอบหน้าใดหน้าหนึ่ง ให้ไปที่หน้าดังกล่าวใน Chrome จากนั้นในเครื่องมือสำหรับนักพัฒนาเว็บ ให้ไปที่แอปพลิเคชัน > แคชย้อนหลัง จากนั้นคลิกปุ่มเรียกใช้การทดสอบ และเครื่องมือสำหรับนักพัฒนาเว็บจะพยายามออกจากหน้าและย้อนกลับเพื่อดูว่าจะกู้คืนหน้าจาก bfcache ได้หรือไม่

แผง Back-Forward Cache ในเครื่องมือสำหรับนักพัฒนาเว็บ

หากสําเร็จ แผงจะรายงานว่า "กู้คืนจาก Back-Forward Cache" ดังนี้

เครื่องมือสำหรับนักพัฒนาเว็บที่รายงานว่าหน้ากู้คืนจาก bfcache สำเร็จแล้ว

หากไม่สำเร็จ แผงจะบ่งชี้ว่าหน้าไม่ได้รับการคืนค่าพร้อมระบุสาเหตุ

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

การรายงานเครื่องมือสำหรับนักพัฒนาเว็บไม่สามารถกู้คืนหน้าจาก bfcache ได้

ในภาพหน้าจอด้านบน การใช้ Listener เหตุการณ์ unload กำลังป้องกันไม่ให้หน้าเว็บมีสิทธิ์ใช้ bfcache คุณสามารถแก้ไขได้โดยเปลี่ยนจาก unload ไปใช้ pagehide แทน ดังนี้

ไม่ควรทำ
window.addEventListener('unload', ...);
ควรทำ
window.addEventListener('pagehide', ...);

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

bfcache ส่งผลต่อข้อมูลวิเคราะห์และการวัดประสิทธิภาพอย่างไร

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

จริงๆ แล้ว คุณน่าจะรายงานการดูหน้าเว็บจากเบราว์เซอร์อื่นที่ใช้ bfcache ต่ำกว่าอยู่แล้ว เพราะไลบรารีการวิเคราะห์ยอดนิยมส่วนใหญ่ไม่ได้ติดตามการคืนค่า bfcache เป็นการดูหน้าเว็บใหม่

หากไม่ต้องการให้จํานวนการดูหน้าเว็บลดลงเนื่องจาก Chrome เปิดใช้ bfcache คุณสามารถรายงานการคืนค่า bfcache เป็นการดูหน้าเว็บ (แนะนำ) ได้โดยฟังเหตุการณ์ pageshow และตรวจสอบพร็อพเพอร์ตี้ persisted

ตัวอย่างต่อไปนี้แสดงวิธีดําเนินการดังกล่าวด้วย Google Analytics ตรรกะสําหรับเครื่องมือการวิเคราะห์อื่นๆ ก็ควรจะคล้ายกันนี้

// Send a pageview when the page is first loaded.
gtag('event', 'page_view');

window.addEventListener('pageshow', (event) => {
  // Send another pageview if the page is restored from bfcache.
  if (event.persisted) {
    gtag('event', 'page_view');
  }
});

การวัดอัตรา Hit ของ bfcache

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

// Send a navigation_type when the page is first loaded.
gtag('event', 'page_view', {
   'navigation_type': performance.getEntriesByType('navigation')[0].type;
});

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    // Send another pageview if the page is restored from bfcache.
    gtag('event', 'page_view', {
      'navigation_type': 'back_forward_cache';
    });
  }
});

เมื่อดูอัตราส่วนของการนำทาง back_forward ต่อ back_forward_cache จะสามารถคำนวณอัตราส่วน bfcache ได้

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

  • เมื่อผู้ใช้ปิดเบราว์เซอร์และเริ่มต้นอีกครั้ง
  • เมื่อผู้ใช้ทำซ้ำแท็บ
  • เมื่อผู้ใช้ปิดแท็บแล้วเปิดแท็บ

ในบางกรณี เบราว์เซอร์บางประเภทอาจเก็บรักษาประเภทการนำทางดั้งเดิมไว้ ดังนั้นอาจแสดงประเภท back_forward แม้ว่ารายการเหล่านี้ไม่ใช่การนำทางแบบย้อนกลับ/ไปข้างหน้า

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

ดังนั้น เจ้าของเว็บไซต์จึงไม่ควรคาดหวังว่าจะมีอัตราส่วน Hit ของ bfcache 100% สําหรับการไปยังส่วนต่างๆ ของ back_forward ทั้งหมด อย่างไรก็ตาม การวัดอัตราส่วนอาจเป็นประโยชน์ในการระบุหน้าเว็บที่หน้านั้นๆ ป้องกันไม่ให้ใช้ bfcache สำหรับการนำทางย้อนกลับและไปข้างหน้าที่มีสัดส่วนสูง

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

การวัดประสิทธิภาพ

bfcache อาจส่งผลเสียต่อเมตริกประสิทธิภาพที่รวบรวมในภาคสนาม โดยเฉพาะเมตริกที่วัดเวลาในการโหลดหน้าเว็บ

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

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

คุณสามารถจัดการกับปัญหานี้ได้หลายวิธี วิธีแรกคือการใส่คำอธิบายประกอบในเมตริกการโหลดหน้าเว็บทั้งหมดด้วยประเภทการนำทางที่เกี่ยวข้อง ได้แก่ navigate, reload, back_forward หรือ prerender วิธีนี้จะช่วยให้คุณตรวจสอบประสิทธิภาพภายในประเภทการนำทางเหล่านี้ได้ต่อไป แม้ว่าการกระจายโดยรวมจะบิดเบือนไปก็ตาม เราแนะนำให้ใช้วิธีนี้กับเมตริกการโหลดหน้าเว็บที่ไม่คำนึงถึงผู้ใช้เป็นหลัก เช่น Time to First Byte (TTFB)

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

ผลกระทบต่อ Core Web Vitals

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

เครื่องมืออย่างเช่นรายงานประสบการณ์ของผู้ใช้ Chrome ที่รวบรวมและรายงานเกี่ยวกับเมตริก Core Web Vitals จะปฏิบัติต่อการคืนค่า bfcache เป็นการเข้าชมหน้าเว็บแยกต่างหากในชุดข้อมูล

และแม้ว่าจะไม่มี API ประสิทธิภาพเว็บสำหรับวัดเมตริกเหล่านี้หลังจากคืนค่า bfcache โดยเฉพาะ (ในขณะนี้) แต่ก็ประมาณค่าโดยใช้ Web API ที่มีอยู่ได้

  • สำหรับ Largest Contentful Paint (LCP) คุณใช้เดลต้าระหว่างการประทับเวลาของเหตุการณ์ pageshow กับการประทับเวลาของเฟรมที่ระบายสีถัดไปได้ (เนื่องจากระบบจะระบายสีองค์ประกอบทั้งหมดในเฟรมพร้อมกัน) โปรดทราบว่าในกรณีของการคืนค่า bfcache นั้น LCP และ FCP จะเหมือนกัน
  • สำหรับ First Input Delay (FID) คุณเพิ่ม Listener เหตุการณ์อีกครั้ง (ตัวเดียวกับที่ใช้โดย polyfill ของ FID) ในเหตุการณ์ pageshow และรายงาน FID เป็นความล่าช้าของอินพุตแรกหลังจากการคืนค่า bfcache
  • สำหรับ Cumulative Layout Shift (CLS) คุณยังใช้ Performance Observer ที่มีอยู่ต่อไปได้ เพียงแค่รีเซ็ตค่า CLS ปัจจุบันเป็น 0

ดูรายละเอียดเพิ่มเติมเกี่ยวกับผลกระทบที่ bfcache มีต่อเมตริกแต่ละรายการได้ที่หน้าคำแนะนำเมตริกของ Core Web Vitals และสำหรับตัวอย่างเฉพาะเกี่ยวกับวิธีใช้งานเมตริกเวอร์ชัน bfcache ในโค้ด โปรดดูPR การเพิ่มเมตริกลงในไลบรารี JS Web-vitals

แหล่งข้อมูลเพิ่มเติม