Back-Forward Cache (หรือ bfcache) เป็นการเพิ่มประสิทธิภาพเบราว์เซอร์ที่เปิดใช้ Instant การนำทางย้อนกลับและไปข้างหน้า ช่วยปรับปรุงประสบการณ์การท่องเว็บ โดยเฉพาะอย่างยิ่งสำหรับผู้ใช้ที่มีเครือข่ายหรืออุปกรณ์ที่ช้า
ในฐานะนักพัฒนาเว็บ คุณจำเป็นต้องเข้าใจวิธีเพิ่มประสิทธิภาพหน้าเว็บสำหรับ bfcache เพื่อให้ผู้ใช้ได้รับผลประโยชน์
ความเข้ากันได้กับเบราว์เซอร์
bfcache ได้รับการสนับสนุนทั้งใน Firefox และ Safari มาหลายปีแล้วทั้งในเดสก์ท็อปและอุปกรณ์เคลื่อนที่
ตั้งแต่เวอร์ชัน 86 เป็นต้นไป Chrome จะเปิดใช้ bfcache สำหรับการนำทางแบบข้ามเว็บไซต์ใน Android สำหรับผู้ใช้กลุ่มเล็กๆ ส่วนรุ่นต่อๆ ไปจะค่อยๆ ทยอยเปิดตัวการสนับสนุนเพิ่มเติม ตั้งแต่เวอร์ชัน 96 ได้เปิดใช้ bfcache สำหรับผู้ใช้ Chrome ทุกคนในเดสก์ท็อปและอุปกรณ์เคลื่อนที่
ข้อมูลเบื้องต้นเกี่ยวกับ bfcache
bfcache เป็นแคชในหน่วยความจำที่จัดเก็บสแนปชอตทั้งหมดของหน้าเว็บ (รวมถึงฮีป JavaScript) ขณะที่ผู้ใช้ออกจากหน้าไป เมื่อหน้าเว็บทั้งหน้าอยู่ในหน่วยความจำ เบราว์เซอร์จะสามารถเรียกคืนหน้านี้ได้อย่างรวดเร็วหากผู้ใช้ตัดสินใจจะกลับเข้ามาใหม่
คุณเข้าชมเว็บไซต์และคลิกลิงก์เพื่อไปยังหน้าอื่นกี่ครั้งแล้ว เพียงแต่นึกว่าไม่ใช่สิ่งที่คุณต้องการและคลิกที่ปุ่มย้อนกลับ ในช่วงเวลาดังกล่าว bfcache สามารถสร้างความแตกต่างอย่างมากต่อความเร็วในการโหลดหน้าเว็บก่อนหน้า ดังนี้
ดูวิดีโอแสดงการทำงานของ Bfcache เพื่อทำความเข้าใจการเพิ่มความเร็วในการไปยังส่วนต่างๆ ต่อไปนี้
ในวิดีโอ ตัวอย่างที่มี bfcache จะเร็วกว่าตัวอย่างที่ไม่มี bfcache เล็กน้อย
bfcache ไม่เพียงเพิ่มความเร็วในการไปยังส่วนต่างๆ แต่ยังลดปริมาณการใช้อินเทอร์เน็ตอีกด้วย เนื่องจากไม่จำเป็นต้องดาวน์โหลดทรัพยากรอีกครั้ง
ข้อมูลการใช้ Chrome แสดงให้เห็นว่าการไปยังส่วนต่างๆ 1 ใน 10 รายการบนเดสก์ท็อป และ 1 ใน 5 รายการบนอุปกรณ์เคลื่อนที่ย้อนกลับหรือไปข้างหน้า เมื่อเปิดใช้งาน bfcache เบราว์เซอร์สามารถกำจัดการโอนข้อมูลและเวลาที่ใช้ในการโหลดสำหรับหน้าเว็บหลายพันล้านหน้าในทุกๆ วัน!
วิธีการทำงานของ "แคช" งาน
"แคช" ที่ bfcache ใช้นั้นแตกต่างจากแคช HTTP ซึ่งมีหน้าที่เพิ่มความเร็วในการไปยังส่วนต่างๆ ซ้ำ bfcache เป็นสแนปชอตของหน้าเว็บทั้งหน้าในหน่วยความจำ ซึ่งรวมถึงฮีป JavaScript ขณะที่แคช HTTP จะมีเพียงการตอบสนองสำหรับคำขอที่สร้างขึ้นก่อนหน้านี้ เนื่องจากพบได้น้อยมากสำหรับคำขอทั้งหมดที่จำเป็นในการโหลดหน้าเว็บเพื่อให้ดำเนินการจากแคช HTTP การเข้าชมซ้ำที่ใช้การคืนค่า bfcache จะเร็วกว่าการนำทางแบบไม่ได้ปรับให้เหมาะสมที่สุดเสมอ
อย่างไรก็ตาม การสร้างสแนปชอตของหน้าเว็บในหน่วยความจำนั้นมีความซับซ้อนบางประการในแง่ของวิธีที่ดีที่สุดในการเก็บรักษาโค้ดที่อยู่ระหว่างดำเนินการ ตัวอย่างเช่น คุณจะจัดการกับการเรียก setTimeout()
อย่างไรหากถึงระยะหมดเวลาขณะที่หน้าเว็บอยู่ใน bfcache
คำตอบคือเบราว์เซอร์จะหยุดตัวจับเวลาที่รอดำเนินการอยู่ชั่วคราวหรือสัญญาที่ยังไม่ได้รับการแก้ไขสำหรับหน้าใน bfcache รวมถึงงานที่รอดำเนินการเกือบทั้งหมดในคิวงาน JavaScript และกลับมาประมวลผลต่อหากหน้าเว็บได้รับการคืนค่าจาก bfcache
ในบางกรณี เช่น การหมดเวลาและการสัญญาว่าจะให้มีความเสี่ยงต่ำพอสมควร แต่ในบางกรณีก็อาจทำให้เกิดความสับสนหรือพฤติกรรมที่ไม่คาดคิดได้ ตัวอย่างเช่น ถ้าเบราว์เซอร์หยุดงานที่จำเป็นซึ่งเป็นส่วนหนึ่งของ IndexedDB ไว้ชั่วคราว ธุรกรรม อาจส่งผลต่อแท็บอื่นๆ ที่เปิดอยู่ในต้นทางเดียวกัน เนื่องจากฐานข้อมูล IndexedDB เดียวกันนั้นสามารถเข้าถึงได้หลายแท็บพร้อมกัน ด้วยเหตุนี้ โดยทั่วไปเบราว์เซอร์จะไม่พยายามแคชหน้าเว็บที่เกิดขึ้นระหว่างการทำธุรกรรม IndexedDB หรือขณะที่ใช้ API ที่อาจส่งผลต่อหน้าอื่นๆ
ดูรายละเอียดเพิ่มเติมว่าการใช้ API ต่างๆ ส่งผลต่อสิทธิ์การใช้ bfcache ของหน้าเว็บอย่างไรได้ที่เพิ่มประสิทธิภาพหน้าเว็บสำหรับ bfcache
bfcache และ iframe
หากหน้าเว็บมี iframe ที่ฝังไว้ iframe เองจะไม่มีสิทธิ์ใช้ bfcache ตัวอย่างเช่น ถ้าคุณนำทางไปยังหน้าอื่นใน iframe แต่ย้อนกลับ เบราว์เซอร์จะ "back" ภายใน iframe ไม่ใช่ในเฟรมหลัก แต่การนำทางกลับภายใน iframe จะไม่ใช้ bfcache
เฟรมหลักอาจถูกบล็อกไม่ให้ใช้ bfcache หาก iframe ที่ฝังใช้ API ที่บล็อกเฟรมหลักดังกล่าว คุณอาจใช้นโยบายสิทธิ์ที่กำหนดไว้ในเฟรมหลักหรือใช้แอตทริบิวต์ sandbox
เพื่อหลีกเลี่ยงปัญหานี้ได้
แอป bfcache และแอปหน้าเดียว (SPA)
เนื่องจาก bfcache ทำงานได้กับการนำทางที่จัดการโดยเบราว์เซอร์ จึงใช้ไม่ได้กับ "การนำทางที่นุ่มนวล" ภายในแอปหน้าเว็บเดียว (SPA) อย่างไรก็ตาม bfcache ยังคงสามารถช่วยได้เมื่อกลับไปใช้ SPA แทนที่จะเริ่มต้นแอปใหม่ทั้งหมดตั้งแต่เริ่มต้น
API สำหรับสังเกต 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
แต่นั่นหมายความว่าเบราว์เซอร์ตั้งใจจะแคชหน้านี้เท่านั้น ระบบอาจยังคงต้องทิ้ง URL ดังกล่าวด้วยเหตุผลหลายประการที่อธิบายในภายหลัง
เพิ่มประสิทธิภาพหน้าเว็บสำหรับ bfcache
หน้าเว็บบางหน้าไม่ได้ถูกจัดเก็บไว้ใน bfcache และแม้ว่าจะจัดเก็บหน้าเว็บไว้ที่นั่น หน้าก็จะไม่อยู่เช่นนั้นต่อไป นักพัฒนาซอฟต์แวร์จำเป็นต้องเข้าใจสิ่งที่ทำให้หน้าเว็บมีสิทธิ์ (และไม่มีสิทธิ์) สำหรับ bfcache เพื่อเพิ่มอัตราการเกิดแคชสูงสุด
ส่วนต่อไปนี้จะสรุปแนวทางปฏิบัติที่ดีที่สุดเพื่อให้เบราว์เซอร์มีโอกาสแคชหน้าเว็บมากที่สุด
ไม่ใช้เหตุการณ์ unload
วิธีที่สำคัญที่สุดในการเพิ่มประสิทธิภาพสำหรับ bfcache ในเบราว์เซอร์ทั้งหมดคือการไม่ใช้เหตุการณ์ unload
ได้เลย!
เหตุการณ์ unload
ทำให้เกิดปัญหาสำหรับเบราว์เซอร์เนื่องจากเกิดก่อน bfcache และหน้าเว็บหลายหน้าในอินเทอร์เน็ตทำงานภายใต้สมมติฐาน (ที่สมเหตุสมผล) ว่าหน้าเว็บจะไม่คงอยู่ต่อไปหลังจากที่เหตุการณ์ unload
เริ่มทำงานแล้ว ซึ่งก่อให้เกิดความท้าทายเนื่องจากหน้าเว็บหลายหน้าเหล่านั้นก็สร้างขึ้นด้วยสมมติฐานที่ว่าเหตุการณ์ unload
จะเริ่มทำงานทุกครั้งที่ผู้ใช้ออกจากหน้าเว็บ ซึ่งไม่เป็นความจริงอีกต่อไป (และไม่เป็นความจริงมานานแล้ว)
เบราว์เซอร์จึงต้องเผชิญกับปัญหายุ่งยาก พวกเขาจึงต้องเลือกว่าจะใช้สิ่งที่สามารถปรับปรุงประสบการณ์ของผู้ใช้แต่อาจทำให้หน้าเว็บเสียหายได้
ในเดสก์ท็อป Chrome และ Firefox ได้เลือกทำให้หน้าเว็บไม่มีสิทธิ์สำหรับ bfcache หากเพิ่ม Listener unload
ซึ่งมีความเสี่ยงน้อยกว่าแต่ก็ทำให้หน้าเว็บหมดสิทธิ์จำนวนมากด้วย Safari จะพยายามแคชบางหน้าด้วย Listener เหตุการณ์ unload
แต่เพื่อลดโอกาสการเสียหายที่อาจเกิดขึ้น Chrome จะไม่เรียกใช้เหตุการณ์ unload
เมื่อผู้ใช้ออกจากหน้าไป ซึ่งทำให้เหตุการณ์ไม่น่าเชื่อถืออย่างมาก
ในอุปกรณ์เคลื่อนที่ Chrome และ Safari จะพยายามแคชหน้าเว็บที่มี Listener เหตุการณ์ unload
เนื่องจากความเสี่ยงในการหยุดทำงานนั้นต่ำกว่าเพราะเหตุการณ์ unload
ไม่น่าเชื่อถืออย่างมากมาโดยตลอดในอุปกรณ์เคลื่อนที่ Firefox จะถือว่าหน้าที่ใช้ unload
ไม่มีสิทธิ์ใช้งาน bfcache ยกเว้นใน iOS ซึ่งกำหนดให้เบราว์เซอร์ทุกชนิดใช้เครื่องมือแสดงผล WebKit ดังนั้นจึงมีลักษณะการทำงานเหมือน Safari
แทนที่จะใช้เหตุการณ์ unload
ให้ใช้เหตุการณ์ pagehide
แทน เหตุการณ์ pagehide
จะเริ่มทำงานทุกครั้งที่เหตุการณ์ unload
เริ่มทำงาน และยังเริ่มทำงานเมื่อมีการวางหน้าเว็บไว้ใน bfcache
ที่จริงแล้ว Lighthouse มีการตรวจสอบ no-unload-listeners
ซึ่งจะเตือนนักพัฒนาซอฟต์แวร์หาก JavaScript ในหน้าเว็บ (รวมถึงจากไลบรารีของบุคคลที่สาม) เพิ่ม Listener เหตุการณ์ unload
Chrome กำลังมองหาวิธีเลิกใช้งานunload
เหตุการณ์อันเนื่องมาจากความไม่เสถียรและผลกระทบด้านประสิทธิภาพของ Bfcache
ใช้นโยบายสิทธิ์เพื่อป้องกันไม่ให้มีการใช้ตัวแฮนเดิลยกเลิกการโหลดในหน้าเว็บ
เว็บไซต์ที่ไม่ได้ใช้ตัวแฮนเดิลเหตุการณ์ unload
สามารถตรวจสอบว่าไม่ได้เพิ่มตัวแฮนเดิลเหล่านี้โดยใช้นโยบายสิทธิ์
Permission-Policy: unload=()
นอกจากนี้ ยังป้องกันไม่ให้บุคคลที่สามหรือส่วนขยายทำให้เว็บไซต์ช้าลงด้วยการเพิ่มตัวจัดการสำหรับยกเลิกการโหลดและทำให้เว็บไซต์ไม่มีสิทธิ์สำหรับ bfcache
เพิ่ม Listener beforeunload
อย่างมีเงื่อนไขเท่านั้น
เหตุการณ์ beforeunload
จะไม่ทำให้หน้าเว็บของคุณไม่มีสิทธิ์ใช้ bfcache ในเบราว์เซอร์สมัยใหม่ bfcache แต่ก่อนหน้านี้ดำเนินการแล้วและยังคงไม่น่าเชื่อถือ ดังนั้นให้หลีกเลี่ยงการใช้เว้นแต่จะจำเป็นจริงๆ
อย่างไรก็ตาม ยังคงมีการใช้งานที่ถูกต้องซึ่งต่างจากเหตุการณ์ unload
beforeunload
เช่น เมื่อต้องการเตือนผู้ใช้ว่า
การเปลี่ยนแปลงที่ยังไม่ได้บันทึกจะสูญหายหากออกจากหน้านี้ ในกรณีนี้ จะเป็น
แนะนำให้คุณเพิ่ม 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 Lock, WebSockets) ปัญหานี้ก็อาจเป็นปัญหาได้เพราะการหยุดงานเหล่านี้ชั่วคราวอาจทำให้โค้ดในแท็บอื่นไม่ทำงาน
ด้วยเหตุนี้ เบราว์เซอร์บางประเภทจะไม่พยายามวางหน้าเว็บใน bfcache ในสถานการณ์ต่อไปนี้
- หน้าเว็บที่มีการเชื่อมต่อ IndexedDB แบบเปิด
- หน้าเว็บที่มี fetch() หรือ XMLHttpRequest ที่อยู่ระหว่างดำเนินการ
- หน้าเว็บที่มีการเชื่อมต่อ WebSocket หรือ WebRTC แบบเปิด
หากหน้าเว็บของคุณใช้ API เหล่านี้ เราขอแนะนำเป็นอย่างยิ่งให้ปิดการเชื่อมต่อและนำผู้สังเกตการณ์ออกหรือยกเลิกการเชื่อมต่อระหว่างเหตุการณ์ pagehide
หรือ freeze
วิธีนี้จะช่วยให้เบราว์เซอร์สามารถแคชหน้าเว็บได้อย่างปลอดภัยโดยไม่ส่งผลกระทบต่อแท็บอื่นๆ ที่เปิดอยู่
จากนั้น หากมีการคืนค่าหน้าจาก bfcache คุณสามารถเปิดหรือเชื่อมต่อกับ API เหล่านั้นอีกครั้งระหว่างเหตุการณ์ pageshow
หรือ resume
ตัวอย่างต่อไปนี้แสดงวิธีตรวจสอบว่าหน้าที่ใช้ IndexedDB มีสิทธิ์ใช้ bfcache ด้วยการปิดการเชื่อมต่อแบบเปิดใน Listener เหตุการณ์ 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 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 ช่วยให้คุณทดสอบหน้าเว็บเพื่อให้มั่นใจว่าหน้าเว็บได้รับการเพิ่มประสิทธิภาพสำหรับ bfcache และระบุปัญหาที่อาจทำให้หน้าเว็บไม่มีสิทธิ์แล้ว
วิธีทดสอบหน้าเว็บ
- ไปที่หน้าเว็บใน Chrome
- ใน DevTools ให้ไปที่แอปพลิเคชัน -> แคชย้อนหลัง
- คลิกปุ่มเรียกใช้การทดสอบ จากนั้นเครื่องมือสำหรับนักพัฒนาเว็บจะพยายามออกและย้อนกลับ เพื่อระบุว่าจะกู้คืนหน้าจาก bfcache ได้ไหม
![แผงแคชย้อนหลังในเครื่องมือสำหรับนักพัฒนาเว็บ](https://web.developers.google.cn/static/articles/bfcache/image/back-forward-cache-panel-fc27405c6cd76.png?authuser=6&hl=th)
หากการทดสอบสำเร็จ แผงจะรายงาน "กู้คืนจากแคชย้อนหลัง"
![เครื่องมือสำหรับนักพัฒนาเว็บรายงานว่ากู้คืนหน้าจาก bfcache สำเร็จแล้ว](https://web.developers.google.cn/static/articles/bfcache/image/devtools-reporting-page-1dd392594f6b2.png?authuser=6&hl=th)
หากไม่สำเร็จ แผงจะแสดงสาเหตุ หากเหตุผลคือสิ่งที่คุณแก้ไขในฐานะนักพัฒนาซอฟต์แวร์ได้ แผงจะทำเครื่องหมายว่าดำเนินการได้
![การรายงานของเครื่องมือสำหรับนักพัฒนาเว็บล้มเหลวในการกู้คืนหน้าจาก bfcache](https://web.developers.google.cn/static/articles/bfcache/image/devtools-reporting-failur-e962032a9c1e2.png?authuser=6&hl=th)
ในตัวอย่างนี้ การใช้ Listener เหตุการณ์ unload
ทำให้หน้าเว็บไม่มีสิทธิ์ใช้ 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');
}
});
วัดอัตราส่วน 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';
});
}
});
คำนวณอัตราส่วน Hit ของ bfcache โดยใช้จำนวนการนำทาง back_forward
รายการและการนำทาง back_forward_cache
รายการ
โปรดทราบว่ามีบางสถานการณ์ที่อยู่นอกเหนือการควบคุมของเจ้าของเว็บไซต์ เมื่อการนำทาง "กลับ/ไปข้างหน้า" ไม่ใช้ bfcache ซึ่งได้แก่
- เมื่อผู้ใช้ออกจากเบราว์เซอร์แล้วเปิดอีกครั้ง
- เมื่อผู้ใช้ทำแท็บซ้ำ
- เมื่อผู้ใช้ปิดแท็บแล้วเปิดอีกครั้ง
ในบางกรณีเหล่านี้ ประเภทการนำทางเดิมอาจยังคงอยู่ในบางเบราว์เซอร์ และอาจแสดงประเภท back_forward
แม้จะไม่ใช่การนำทางกลับ/ไปข้างหน้าก็ตาม
แม้ว่าจะไม่มีการยกเว้นดังกล่าว แต่ bfcache จะถูกทิ้งหลังจากผ่านไประยะหนึ่งเพื่อประหยัดหน่วยความจำ
ดังนั้นเจ้าของเว็บไซต์จึงไม่ควรคาดหวังอัตราการเกิด 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 กู้คืน แต่คุณสามารถประมาณค่าได้โดยใช้ API ของเว็บที่มีอยู่
- สำหรับ Largest Contentful Paint (LCP) ให้ใช้เดลต้าระหว่างการประทับเวลาของเหตุการณ์
pageshow
กับการประทับเวลาของเฟรมที่วาดภาพถัดไป เนื่องจากระบบจะแสดงผลองค์ประกอบทั้งหมดในเฟรมพร้อมกัน ในกรณีการกู้คืน bfcache นั้น LCP และ FCP จะเหมือนกัน - สำหรับ Interaction to Next Paint (INP) ให้ใช้ Performance Observer ที่มีอยู่ต่อไป แต่รีเซ็ตค่า INP ปัจจุบันเป็น 0
- สำหรับ Cumulative Layout Shift (CLS) ให้ใช้ Performance Observer ที่มีอยู่ต่อไป แต่รีเซ็ตค่า CLS ปัจจุบันเป็น 0
ดูรายละเอียดเพิ่มเติมเกี่ยวกับวิธีที่ bfcache ส่งผลต่อเมตริกแต่ละรายการได้ที่หน้าคำแนะนำเมตริกของ Core Web Vitals ดูตัวอย่างที่เฉพาะเจาะจงเกี่ยวกับวิธีใช้งานเมตริกเหล่านี้ในเวอร์ชัน Bfcache ได้จากการประชาสัมพันธ์การเพิ่มเมตริกลงในไลบรารี JS เวอร์ชันเว็บ
ไลบรารี JavaScript เว็บไวทัลจะรองรับการคืนค่า bfcache ในเมตริกที่รายงาน
แหล่งข้อมูลเพิ่มเติม
- การแคช Firefox (bfcache ใน Firefox)
- แคชของหน้าเว็บ (bfcache ใน Safari)
- Back-Forward Cache: ลักษณะการทำงานที่เปิดเผยบนเว็บ (ความแตกต่างของ Bfcache ในเบราว์เซอร์ต่างๆ)
- ผู้ทดสอบ bfcache (ทดสอบว่า API และเหตุการณ์ต่างๆ ส่งผลต่อ bfcache ในเบราว์เซอร์อย่างไร)
- ตัวเปลี่ยนเกมด้านประสิทธิภาพ: แคชย้อนหลังของเบราว์เซอร์ (กรณีศึกษาจาก Smashing Magazine แสดงการปรับปรุง Core Web Vitals ที่น่าทึ่งโดยการเปิดใช้ bfcache)