Back-Forward Cache

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

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

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

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

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

เมื่อใช้แคช "ย้อนกลับ/ไปข้างหน้า" (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 ตลอดไป นักพัฒนาซอฟต์แวร์จําเป็นต้องเข้าใจปัจจัยที่ทำให้หน้าเว็บมีสิทธิ์ (หรือไม่มีสิทธิ์) สำหรับ 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 สามารถตรวจสอบได้ว่าไม่มีการเพิ่มตัวแฮนเดิลเหล่านี้โดยใช้นโยบายสิทธิ์

Permissions-Policy: unload=()

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

เพิ่มผู้ฟังตามเงื่อนไขเพียง beforeunload รายเท่านั้น

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

อย่างไรก็ตาม beforeunload มีการใช้งานที่ถูกต้องตามกฎหมาย ซึ่งต่างจากเหตุการณ์ unload เช่น เมื่อต้องการเตือนผู้ใช้ว่ามีการเปลี่ยนแปลงที่ยังไม่ได้บันทึกซึ่งจะหายไปหากออกจากหน้า ในกรณีนี้ เราขอแนะนําให้เพิ่มbeforeunload Listeners เฉพาะเมื่อผู้ใช้มีการเปลี่ยนแปลงที่ไม่ได้บันทึกไว้ แล้วนําออกทันทีหลังจากบันทึกการเปลี่ยนแปลงที่ไม่ได้บันทึกไว้

ไม่ควรทำ
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);
});
โค้ดนี้จะเพิ่ม beforeunload listener เฉพาะเมื่อจําเป็นเท่านั้น (และนําออกเมื่อไม่จําเป็น)

ลดการใช้ 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 ล้มเหลวพร้อมผลลัพธ์ที่นําไปใช้ได้

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

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

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

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

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

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

หากต้องการรวมการกู้คืน bfcache ไว้ในจํานวนการดูหน้าเว็บ ให้ตั้งค่า Listeners สําหรับเหตุการณ์ 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) ให้ใช้เครื่องมือสังเกตประสิทธิภาพที่มีอยู่ต่อไป แต่รีเซ็ตค่า CLS ปัจจุบันเป็น 0

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

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

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