Back-Forward Cache

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

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

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

เบราว์เซอร์หลักๆ ทั้งหมดมี bfcache ซึ่งรวมถึง Chrome ตั้งแต่เวอร์ชัน 96, Firefox และ Safari

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

เมื่อใช้ Back-Forward Cache (bfcache) เราจะเลื่อนการทำลายและหยุดการดำเนินการ JS ไว้ชั่วคราวแทนการทำลายหน้าเมื่อผู้ใช้ออกจากหน้า หากผู้ใช้กลับไปที่หน้าเว็บในไม่ช้า เราจะแสดงหน้าเว็บอีกครั้งและยกเลิกการหยุดการทํางานของ JS ซึ่งส่งผลให้ผู้ใช้ไปยังส่วนต่างๆ ของหน้าเว็บได้เกือบจะทันที

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

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

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

การใช้ bfcache ทําให้หน้าเว็บโหลดเร็วขึ้นมากในระหว่างการไปยังส่วนต่างๆ แบบย้อนกลับ

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

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

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

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

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

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

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

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

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

bfcache และ iframe

หากหน้าเว็บมี iframe ที่ฝังไว้ iframe เองจะไม่มีสิทธิ์ใช้ bfcache ตัวอย่างเช่น หากคุณไปยังหน้าอื่นภายใน iframe แล้วกลับไป เบราว์เซอร์จะ "กลับ" ภายใน iframe แทนที่จะกลับในเฟรมหลัก แต่การไปยังส่วนต่างๆ กลับภายใน iframe จะไม่ใช้ bfcache

นอกจากนี้ ระบบยังบล็อกเฟรมหลักไม่ให้ใช้ bfcache ได้หาก iframe ที่ฝังใช้ API ที่บล็อกการดำเนินการนี้ คุณอาจใช้นโยบายสิทธิ์ที่กำหนดไว้ในเฟรมหลักหรือการใช้แอตทริบิวต์ sandbox เพื่อหลีกเลี่ยงปัญหานี้ได้

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

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

API สําหรับสังเกต bfcache

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

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

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

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

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

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

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 แต่นั่นหมายความว่าเบราว์เซอร์ตั้งใจแคชหน้าเว็บเท่านั้น ระบบอาจยังต้องทิ้งคำสั่งซื้อดังกล่าวด้วยเหตุผลหลายประการที่อธิบายไว้ภายหลัง

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

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

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

ไม่ใช้เหตุการณ์ unload

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

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

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

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

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

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

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

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

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

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

Permission-Policy: unload=()

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

เพิ่มผู้ฟังตามเงื่อนไขเพียง 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?';
  }
});
รหัสนี้จะเพิ่มผู้ฟัง 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 ดังนั้นหน้าเว็บที่ใช้ Cache-Control: no-store จึงอาจไม่มีสิทธิ์ใช้ bfcache เรากำลังดำเนินการเปลี่ยนแปลงลักษณะการทํางานนี้สําหรับ Chrome ในลักษณะที่รักษาความเป็นส่วนตัว

เนื่องจาก 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 ที่ไม่ใช่ค่า Null ยังไม่สามารถใส่ลงใน bfcache ได้เนื่องจากอาจทำให้หน้าเว็บที่พยายามเข้าถึงหน้านั้นใช้งานไม่ได้

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

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

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

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

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

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

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

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

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

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

วิธีทดสอบหน้าเว็บ

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

หากการทดสอบสําเร็จ แผงจะแสดงข้อความ "กู้คืนจากแคชย้อนกลับ"

เครื่องมือสําหรับนักพัฒนาซอฟต์แวร์รายงานว่ากู้คืนหน้าเว็บจาก bfcache เรียบร้อยแล้ว
กู้คืนหน้าเรียบร้อยแล้ว

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

เครื่องมือสำหรับนักพัฒนาเว็บรายงานว่าคืนค่าหน้าเว็บจาก bfcache ไม่สำเร็จ
การทดสอบ bfcache ล้มเหลวพร้อมผลลัพธ์ที่นําไปใช้ได้

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

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

นอกจากนี้ Lighthouse 10.0 ยังเพิ่มการตรวจสอบ bfcache ซึ่งทำการทดสอบที่คล้ายกัน ดูข้อมูลเพิ่มเติมได้ที่เอกสารของการตรวจสอบ bfcache

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

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

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

หากต้องการรวมการกู้คืน bfcache ไว้ในจำนวนการดูหน้าเว็บ ให้ตั้งค่า Listener สำหรับเหตุการณ์ 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');
  }
});

วัดอัตราส่วนการค้นพบ 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';
    });
  }
});

คํานวณอัตรา Hit ของ bfcache โดยใช้จํานวนการนําทาง back_forward และการนําทาง back_forward_cache

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

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

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

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

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

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

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

  • สำหรับ Largest Contentful Paint (LCP) ให้ใช้เดลต้าระหว่างการประทับเวลาของเหตุการณ์ pageshow กับการประทับเวลาของเฟรมที่วาดภาพถัดไป เนื่องจากระบบจะแสดงผลองค์ประกอบทั้งหมดในเฟรมพร้อมกัน ในกรณีการกู้คืน bfcache นั้น LCP และ FCP จะเหมือนกัน
  • สําหรับ Interaction to Next Paint (INP) ให้ใช้เครื่องมือสังเกตประสิทธิภาพที่มีอยู่ต่อไป แต่รีเซ็ตค่า INP ปัจจุบันเป็น 0
  • สำหรับ Cumulative Layout Shift (CLS) ให้ใช้ Performance Observer ที่มีอยู่ต่อไป แต่รีเซ็ตค่า CLS ปัจจุบันเป็น 0

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

ไลบรารี JavaScript ของ web-vitals รองรับการกู้คืน bfcache ในเมตริกที่รายงาน

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