Back-Forward Cache

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

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

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

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

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

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

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

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

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

ดูวิดีโอแสดงการทำงานของ 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 ในสถานการณ์ต่อไปนี้

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

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

  1. ไปที่หน้าเว็บใน Chrome
  2. ใน DevTools ให้ไปที่แอปพลิเคชัน -> แคชย้อนหลัง
  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 ไว้ในจำนวนการดูหน้าเว็บ ให้ตั้งค่า 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 web-vitalsจะรองรับการคืนค่า bfcache ในเมตริกที่รายงาน

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