Browser Support
ปัจจุบันเบราว์เซอร์ที่ทันสมัยจะระงับหน้าเว็บหรือทิ้งหน้าเว็บไปเลยในบางครั้งเมื่อ ทรัพยากรของระบบมีจำกัด ในอนาคต เบราว์เซอร์ต้องการดำเนินการนี้ โดยอัตโนมัติเพื่อลดการใช้พลังงานและหน่วยความจำ Page Lifecycle API มีฮุกวงจรเพื่อให้หน้าเว็บจัดการการแทรกแซงของเบราว์เซอร์เหล่านี้ได้อย่างปลอดภัยโดยไม่ส่งผลต่อประสบการณ์ของผู้ใช้ ดู API เพื่อ ดูว่าคุณควรใช้ฟีเจอร์เหล่านี้ในแอปพลิเคชันหรือไม่
ฉากหลัง
วงจรแอปพลิเคชันเป็นวิธีสำคัญที่ระบบปฏิบัติการสมัยใหม่ใช้จัดการ ทรัพยากร ใน Android, iOS และ Windows เวอร์ชันล่าสุด ระบบปฏิบัติการสามารถเริ่มและหยุดแอปได้ทุกเมื่อ ซึ่งช่วยให้แพลตฟอร์มเหล่านี้เพิ่มประสิทธิภาพและ จัดสรรทรัพยากรใหม่ในจุดที่ผู้ใช้ได้รับประโยชน์มากที่สุด
ในเว็บนั้นไม่มีวงจรดังกล่าวในอดีต และแอปสามารถทำงานได้ตลอดเวลา เมื่อมีหน้าเว็บจำนวนมากทำงานอยู่ ระบบอาจใช้ทรัพยากรที่สำคัญ เช่น หน่วยความจำ, CPU, แบตเตอรี่ และเครือข่าย มากเกินไป ซึ่งส่งผลให้ผู้ใช้ปลายทางได้รับประสบการณ์การใช้งานที่ไม่ดี
แม้ว่าแพลตฟอร์มเว็บจะมีเหตุการณ์ที่เกี่ยวข้องกับสถานะวงจรการใช้งานมานานแล้ว
เช่น load
unload และ
visibilitychange
แต่เหตุการณ์เหล่านี้อนุญาตให้นักพัฒนาแอป
ตอบสนองต่อการเปลี่ยนแปลงสถานะวงจรการใช้งานที่ผู้ใช้เป็นผู้เริ่มเท่านั้น เพื่อให้เว็บทำงานได้อย่าง
น่าเชื่อถือในอุปกรณ์ที่มีกำลังไฟต่ำ (และใช้ทรัพยากรอย่างมีประสิทธิภาพมากขึ้นโดยทั่วไปใน
ทุกแพลตฟอร์ม) เบราว์เซอร์จึงต้องมีวิธีในการเรียกคืนและจัดสรรทรัพยากรของระบบใหม่ในเชิงรุก
ในความเป็นจริง ปัจจุบันเบราว์เซอร์ใช้มาตรการเชิงรุกเพื่อประหยัดทรัพยากร สำหรับหน้าเว็บในแท็บพื้นหลังอยู่แล้ว และเบราว์เซอร์หลายตัว (โดยเฉพาะ Chrome) ต้องการ ทำสิ่งนี้ให้มากขึ้นเพื่อลดร่องรอยการใช้ทรัพยากรโดยรวม
ปัญหาคือ นักพัฒนาแอปไม่มีวิธีเตรียมพร้อมรับการแทรกแซงประเภทนี้ที่ระบบเป็นผู้เริ่ม หรือแม้แต่ทราบว่าการแทรกแซงเกิดขึ้น ซึ่งหมายความว่าเบราว์เซอร์ต้องระมัดระวังหรือเสี่ยงต่อการทำให้หน้าเว็บเสียหาย
Page Lifecycle API พยายามแก้ปัญหานี้โดยทำดังนี้
- การเปิดตัวและกำหนดมาตรฐานแนวคิดของสถานะวงจรผลิตภัณฑ์บนเว็บ
- การกำหนดสถานะใหม่ที่ระบบเริ่มต้นซึ่งอนุญาตให้เบราว์เซอร์จำกัดทรัพยากรที่แท็บที่ซ่อนอยู่หรือไม่ได้ใช้งานใช้ได้
- สร้าง API และเหตุการณ์ใหม่ๆ ที่ช่วยให้นักพัฒนาเว็บตอบสนองต่อ การเปลี่ยนจากและไปยังสถานะใหม่ที่ระบบเริ่มต้นเหล่านี้ได้
โซลูชันนี้ช่วยให้เว็บดีเวลอปเปอร์คาดการณ์ได้ตามที่ต้องการเพื่อสร้างแอปพลิเคชันที่ทนทานต่อการแทรกแซงระบบ และช่วยให้เบราว์เซอร์เพิ่มประสิทธิภาพทรัพยากรของระบบได้อย่างมีประสิทธิภาพมากขึ้น ซึ่งท้ายที่สุดแล้วจะเป็นประโยชน์ต่อผู้ใช้เว็บทุกคน
ส่วนที่เหลือของโพสต์นี้จะแนะนำฟีเจอร์วงจรหน้าเว็บใหม่ และสำรวจความเกี่ยวข้องกับสถานะและเหตุการณ์ทั้งหมดที่มีอยู่ของแพลตฟอร์มเว็บ นอกจากนี้ ยังจะให้คำแนะนำและแนวทางปฏิบัติแนะนำสำหรับประเภท ของงานที่นักพัฒนาซอฟต์แวร์ควร (และไม่ควร) ทำในแต่ละสถานะ
ภาพรวมของสถานะและเหตุการณ์ในวงจรของหน้าเว็บ
สถานะวงจรหน้าเว็บทั้งหมดจะแยกกันและไม่ทับซ้อนกัน ซึ่งหมายความว่าหน้าเว็บ จะอยู่ในสถานะได้เพียงสถานะเดียวในแต่ละครั้ง และโดยทั่วไปแล้วการเปลี่ยนแปลงสถานะวงจรของหน้าเว็บส่วนใหญ่ จะสังเกตได้ผ่านเหตุการณ์ DOM (ดูข้อยกเว้นได้ที่คำแนะนำสำหรับนักพัฒนาซอฟต์แวร์สำหรับแต่ละสถานะ)
วิธีที่ง่ายที่สุดในการอธิบายสถานะวงจรหน้าเว็บ รวมถึง เหตุการณ์ที่ส่งสัญญาณการเปลี่ยนสถานะระหว่างกันอาจเป็นแผนภาพต่อไปนี้
รัฐ
ตารางต่อไปนี้จะอธิบายแต่ละสถานะโดยละเอียด นอกจากนี้ ยังแสดงสถานะที่เป็นไปได้ ซึ่งอาจเกิดขึ้นก่อนและหลัง รวมถึงเหตุการณ์ที่นักพัฒนาแอปใช้ เพื่อสังเกตการเปลี่ยนแปลงได้
| รัฐ | คำอธิบาย |
|---|---|
| ใช้งานอยู่ |
หน้าเว็บจะอยู่ในสถานะใช้งานอยู่หากมองเห็นได้และมี โฟกัสอินพุต
สถานะก่อนหน้าที่เป็นไปได้ |
| เชิงรับ |
หน้าเว็บจะอยู่ในสถานะพาสซีฟหากมองเห็นได้และไม่มีโฟกัสอินพุต
สถานะก่อนหน้าที่เป็นไปได้
สถานะถัดไปที่เป็นไปได้ |
| ซ่อน |
หน้าเว็บจะอยู่ในสถานะซ่อนหากมองไม่เห็น (และยังไม่ได้ ถูกตรึง ถูกทิ้ง หรือถูกปิด)
สถานะก่อนหน้าที่เป็นไปได้
สถานะถัดไปที่เป็นไปได้ |
| ค้าง |
ในสถานะหยุดชั่วคราว เบราว์เซอร์จะระงับการดำเนินการของ
freezable
tasks ใน
คิวของงานของหน้าเว็บจนกว่าจะยกเลิกการหยุดชั่วคราวของหน้าเว็บ ซึ่งหมายความว่าสิ่งต่างๆ เช่น
ตัวจับเวลา JavaScript และการเรียกกลับของฟีเจอร์ Fetch จะไม่ทำงาน งานที่กำลังทำงานอยู่
จะทำงานจนเสร็จได้ (ที่สำคัญที่สุดคือการเรียกกลับ
เบราว์เซอร์จะหยุดการทำงานของหน้าเว็บเพื่อรักษาการใช้งาน CPU/แบตเตอรี่/อินเทอร์เน็ต และยังทำเพื่อช่วยให้ การไปยังหน้าก่อนหน้า/ถัดไปเร็วขึ้นด้วย ซึ่งจะช่วยให้ไม่ต้องโหลดหน้าเว็บซ้ำทั้งหมด
สถานะก่อนหน้าที่เป็นไปได้
สถานะถัดไปที่เป็นไปได้ |
| สิ้นสุด |
หน้าเว็บจะอยู่ในสถานะสิ้นสุดเมื่อเบราว์เซอร์เริ่ม ยกเลิกการโหลดและล้างออกจากหน่วยความจำ ไม่ งานใหม่จะเริ่มในสถานะนี้ไม่ได้ และระบบอาจ หยุดงานที่กำลังดำเนินการอยู่หากใช้เวลานานเกินไป
สถานะก่อนหน้าที่เป็นไปได้
สถานะถัดไปที่เป็นไปได้: |
| ทิ้ง |
หน้าเว็บจะอยู่ในสถานะถูกละทิ้งเมื่อเบราว์เซอร์ยกเลิกการโหลดเพื่อประหยัดทรัพยากร ไม่มีงาน การเรียกกลับของเหตุการณ์ หรือ JavaScript ใดๆ ที่จะทำงานในสถานะนี้ได้ เนื่องจากโดยปกติแล้วการทิ้งจะ เกิดขึ้นภายใต้ข้อจำกัดด้านทรัพยากร ซึ่งการเริ่มกระบวนการใหม่เป็นไป ไม่ได้ ในสถานะทิ้ง โดยปกติแล้วผู้ใช้จะเห็นแท็บเอง (รวมถึงชื่อแท็บและ favicon) แม้ว่าหน้าจะหายไปแล้วก็ตาม
สถานะก่อนหน้าที่เป็นไปได้:
สถานะถัดไปที่เป็นไปได้: |
กิจกรรม
เบราว์เซอร์จะส่งเหตุการณ์จำนวนมาก แต่มีเพียงส่วนเล็กๆ เท่านั้นที่ส่งสัญญาณถึง การเปลี่ยนแปลงสถานะวงจรหน้าเว็บที่อาจเกิดขึ้น ตารางต่อไปนี้สรุปเหตุการณ์ทั้งหมด ที่เกี่ยวข้องกับวงจร และแสดงรายการสถานะที่เหตุการณ์อาจเปลี่ยนไปและเปลี่ยนจาก
| ชื่อ | รายละเอียด |
|---|---|
focus
|
องค์ประกอบ DOM ได้รับโฟกัส
หมายเหตุ: เหตุการณ์
สถานะก่อนหน้าที่เป็นไปได้:
สถานะปัจจุบันที่เป็นไปได้: |
blur
|
องค์ประกอบ DOM สูญเสียโฟกัส
หมายเหตุ: เหตุการณ์
สถานะก่อนหน้าที่เป็นไปได้
สถานะปัจจุบันที่เป็นไปได้: |
visibilitychange
|
ค่าของ
|
freeze
*
|
เพิ่งมีการหยุดการทำงานของหน้าเว็บ ระบบจะไม่เริ่มงานที่ หยุดชั่วคราวได้ในคิวของงานของหน้าเว็บ
สถานะก่อนหน้าที่เป็นไปได้:
สถานะปัจจุบันที่เป็นไปได้: |
resume
*
|
เบราว์เซอร์ได้กลับมาทำงานในหน้าเว็บที่หยุดทำงานแล้ว
สถานะก่อนหน้าที่เป็นไปได้:
สถานะปัจจุบันที่เป็นไปได้ |
pageshow
|
กำลังข้ามไปยังรายการประวัติเซสชัน ซึ่งอาจเป็นการโหลดหน้าเว็บใหม่หรือหน้าเว็บที่นำมาจาก
Back-Forward Cache หากนำหน้ามาจาก Back-Forward Cache พร็อพเพอร์ตี้
สถานะก่อนหน้าที่เป็นไปได้: |
pagehide
|
กำลังข้ามรายการประวัติเซสชัน หากผู้ใช้ไปยังหน้าอื่นและเบราว์เซอร์เพิ่มหน้าปัจจุบันลงในแคชย้อนหลัง/ไปข้างหน้าเพื่อนำกลับมาใช้ใหม่ในภายหลังได้ พร็อพเพอร์ตี้
สถานะก่อนหน้าที่เป็นไปได้:
สถานะปัจจุบันที่เป็นไปได้ |
beforeunload
|
ระบบกำลังจะยกเลิกการโหลดหน้าต่าง เอกสาร และทรัพยากรของเอกสาร เอกสารจะยังคงปรากฏและคุณยังยกเลิกกิจกรรมได้ในตอนนี้
สำคัญ: ควรใช้เหตุการณ์
สถานะก่อนหน้าที่เป็นไปได้:
สถานะปัจจุบันที่เป็นไปได้: |
unload
|
กำลังยกเลิกการโหลดหน้าเว็บ
คำเตือน:
ไม่แนะนำให้ใช้เหตุการณ์
สถานะก่อนหน้าที่เป็นไปได้:
สถานะปัจจุบันที่เป็นไปได้: |
* ระบุเหตุการณ์ใหม่ที่กำหนดโดย Page Lifecycle API
ฟีเจอร์ใหม่ที่เพิ่มเข้ามาใน Chrome 68
แผนภูมิก่อนหน้านี้แสดงสถานะ 2 สถานะที่ระบบเป็นผู้เริ่มต้น ไม่ใช่ผู้ใช้ ได้แก่ frozen และ discarded ดังที่ได้กล่าวไว้ก่อนหน้านี้ ปัจจุบันเบราว์เซอร์จะหยุดทำงานและทิ้งแท็บที่ซ่อนอยู่เป็นครั้งคราว (ตามที่เห็นสมควร) แต่นักพัฒนาแอปไม่สามารถทราบได้ว่าเหตุการณ์นี้เกิดขึ้นเมื่อใด
ใน Chrome 68 ตอนนี้นักพัฒนาซอฟต์แวร์สามารถสังเกตได้เมื่อแท็บที่ซ่อนอยู่ถูกระงับและ
เลิกการระงับโดยการฟังเหตุการณ์ freeze
และ resume ใน document
document.addEventListener('freeze', (event) => {
// The page is now frozen.
});
document.addEventListener('resume', (event) => {
// The page has been unfrozen.
});
ตั้งแต่ Chrome 68 เป็นต้นไป ออบเจ็กต์ document จะมีพร็อพเพอร์ตี้
wasDiscarded
ใน Chrome บนเดสก์ท็อป (เรากำลังติดตามการรองรับ Android ในปัญหานี้) หากต้องการดูว่าระบบทิ้งหน้าเว็บขณะอยู่ในแท็บที่ซ่อนอยู่หรือไม่
คุณสามารถตรวจสอบค่าของพร็อพเพอร์ตี้นี้ในเวลาที่โหลดหน้าเว็บ (โปรดทราบว่า
ต้องโหลดหน้าเว็บที่ถูกทิ้งซ้ำเพื่อใช้งานอีกครั้ง)
if (document.wasDiscarded) {
// Page was previously discarded by the browser while in a hidden tab.
}
ดูคำแนะนำเกี่ยวกับสิ่งที่ควรทำในfreezeและresume
เหตุการณ์ รวมถึงวิธีจัดการและเตรียมพร้อมสำหรับหน้าเว็บที่ถูกทิ้งได้ที่
คำแนะนำสำหรับนักพัฒนาซอฟต์แวร์สำหรับแต่ละสถานะ
ส่วนถัดไปจะให้ภาพรวมของวิธีที่ฟีเจอร์ใหม่เหล่านี้ทำงานร่วมกับสถานะและเหตุการณ์ของแพลตฟอร์มเว็บที่มีอยู่
วิธีสังเกตสถานะวงจรของหน้าเว็บในโค้ด
ในสถานะใช้งานอยู่ ไม่ได้ใช้งาน และซ่อนอยู่ คุณสามารถเรียกใช้โค้ด JavaScript ที่กำหนดสถานะวงจรหน้าเว็บปัจจุบันจาก API ของแพลตฟอร์มเว็บที่มีอยู่ได้
const getState = () => {
if (document.visibilityState === 'hidden') {
return 'hidden';
}
if (document.hasFocus()) {
return 'active';
}
return 'passive';
};
ส่วนสถานะหยุดชั่วคราวและสิ้นสุดจะตรวจหาได้ในเครื่องมือฟังเหตุการณ์ที่เกี่ยวข้อง (freeze และ pagehide) เท่านั้น เนื่องจากสถานะกำลังเปลี่ยนแปลง
วิธีสังเกตการเปลี่ยนแปลงสถานะ
คุณสามารถดูการเปลี่ยนแปลงสถานะ Page
Lifecycle ทั้งหมดได้ด้วยโค้ดต่อไปนี้ โดยอิงตามฟังก์ชัน getState() ที่กำหนดไว้ก่อนหน้านี้
// Stores the initial state using the `getState()` function (defined above).
let state = getState();
// Accepts a next state and, if there's been a state change, logs the
// change to the console. It also updates the `state` value defined above.
const logStateChange = (nextState) => {
const prevState = state;
if (nextState !== prevState) {
console.log(`State change: ${prevState} >>> ${nextState}`);
state = nextState;
}
};
// Options used for all event listeners.
const opts = {capture: true};
// These lifecycle events can all use the same listener to observe state
// changes (they call the `getState()` function to determine the next state).
['pageshow', 'focus', 'blur', 'visibilitychange', 'resume'].forEach((type) => {
window.addEventListener(type, () => logStateChange(getState()), opts);
});
// The next two listeners, on the other hand, can determine the next
// state from the event itself.
window.addEventListener('freeze', () => {
// In the freeze event, the next state is always frozen.
logStateChange('frozen');
}, opts);
window.addEventListener('pagehide', (event) => {
// If the event's persisted property is `true` the page is about
// to enter the back/forward cache, which is also in the frozen state.
// If the event's persisted property is not `true` the page is
// about to be unloaded.
logStateChange(event.persisted ? 'frozen' : 'terminated');
}, opts);
โค้ดนี้ทำ 3 สิ่งต่อไปนี้
- ตั้งค่าสถานะเริ่มต้นโดยใช้ฟังก์ชัน
getState() - กำหนดฟังก์ชันที่ยอมรับสถานะถัดไป และหากมีการเปลี่ยนแปลง จะบันทึกการเปลี่ยนแปลงสถานะไปยังคอนโซล
- เพิ่ม Listener เหตุการณ์สำหรับเหตุการณ์วงจรที่จำเป็นทั้งหมด ซึ่งจะเรียก
logStateChange()และส่งสถานะถัดไป
สิ่งหนึ่งที่ควรทราบเกี่ยวกับโค้ดคือมีการเพิ่ม Listener เหตุการณ์ทั้งหมด
ลงใน window และทั้งหมดจะส่งผ่าน
{capture: true}
ซึ่งอาจเป็นเพราะสาเหตุต่อไปนี้
- เหตุการณ์วงจรหน้าเว็บบางรายการอาจมีเป้าหมายต่างกัน
pagehideและpageshowจะทริกเกอร์ในwindowส่วนvisibilitychange,freezeและresumeจะทริกเกอร์ในdocumentและfocusกับblurจะทริกเกอร์ในองค์ประกอบ DOM ที่เกี่ยวข้อง - เหตุการณ์ส่วนใหญ่เหล่านี้จะไม่ฟอง ซึ่งหมายความว่าคุณจะเพิ่ม Listener เหตุการณ์ที่ไม่จับภาพลงในองค์ประกอบบรรพบุรุษทั่วไปและสังเกตเหตุการณ์ทั้งหมด ไม่ได้
- โดยเฟสการจับภาพจะทำงานก่อนเฟสเป้าหมายหรือเฟสบับเบิล ดังนั้นการเพิ่ม Listener ในเฟสนี้จะช่วยให้มั่นใจได้ว่า Listener จะทำงานก่อนที่โค้ดอื่นๆ จะยกเลิก Listener ได้
คำแนะนำสำหรับนักพัฒนาแอปสำหรับแต่ละรัฐ
ในฐานะนักพัฒนาแอป คุณควรทำความเข้าใจสถานะวงจรหน้าเว็บและ รู้วิธีสังเกตสถานะเหล่านั้นในโค้ด เนื่องจากประเภทงานที่คุณควร (และไม่ควร) ทำนั้นขึ้นอยู่กับสถานะของหน้าเว็บเป็นอย่างมาก
ตัวอย่างเช่น การแสดงการแจ้งเตือนชั่วคราวต่อผู้ใช้เมื่อหน้าเว็บอยู่ในสถานะซ่อนอยู่เป็นสิ่งที่ไม่สมเหตุสมผลอย่างชัดเจน แม้ว่าตัวอย่างนี้จะค่อนข้าง ชัดเจน แต่ก็มีคำแนะนำอื่นๆ ที่ไม่ชัดเจนนักซึ่งควร ระบุไว้
| รัฐ | คำแนะนำสำหรับนักพัฒนาแอป |
|---|---|
Active |
สถานะใช้งานเป็นช่วงเวลาที่สําคัญที่สุดสําหรับผู้ใช้ และเป็นช่วงเวลาที่สําคัญที่สุดสําหรับหน้าเว็บของคุณในการ ตอบสนองต่ออินพุตของผู้ใช้ ควรลดลำดับความสำคัญของงานที่ไม่ใช่ UI ซึ่งอาจบล็อกเทรดหลัก เป็น ช่วงที่ไม่มีการใช้งานหรือ ส่งไปยัง Web Worker |
Passive |
ในสถานะพาสซีฟ ผู้ใช้ไม่ได้โต้ตอบกับหน้าเว็บ แต่ยังคงเห็นหน้าเว็บ ซึ่งหมายความว่าการอัปเดต UI และภาพเคลื่อนไหวควรยังคง ราบรื่น แต่เวลาที่การอัปเดตเหล่านี้เกิดขึ้นมีความสำคัญน้อยลง เมื่อหน้าเว็บเปลี่ยนจากใช้งานอยู่เป็นไม่ได้ใช้งาน ก็เป็นเวลาที่เหมาะที่จะบันทึกสถานะแอปพลิเคชันที่ยังไม่ได้บันทึก |
|
เมื่อหน้าเว็บเปลี่ยนจากไม่ได้ใช้งานเป็นซ่อน ผู้ใช้อาจไม่โต้ตอบกับหน้าเว็บนั้นอีกจนกว่าจะโหลดซ้ำ การเปลี่ยนไปเป็นซ่อนมักเป็นการเปลี่ยนแปลงสถานะครั้งสุดท้าย
ที่นักพัฒนาแอปสังเกตได้อย่างน่าเชื่อถือ (โดยเฉพาะอย่างยิ่งในอุปกรณ์
เคลื่อนที่ เนื่องจากผู้ใช้สามารถปิดแท็บหรือแอปเบราว์เซอร์เองได้ และระบบจะไม่ทริกเกอร์เหตุการณ์ ซึ่งหมายความว่าคุณควรพิจารณาสถานะซ่อนเป็นจุดสิ้นสุดที่เป็นไปได้ของเซสชันผู้ใช้ กล่าวคือ ให้คงสถานะแอปพลิเคชันที่ยังไม่ได้บันทึก และส่งข้อมูลวิเคราะห์ที่ยังไม่ได้ส่ง นอกจากนี้ คุณควรหยุดการอัปเดต UI (เนื่องจากผู้ใช้จะไม่เห็น) และหยุดงานใดๆ ที่ผู้ใช้ไม่ต้องการให้ทำงานในเบื้องหลัง |
|
Frozen |
ในสถานะหยุดชั่วคราว งานที่หยุดชั่วคราวได้ใน คิวของงานจะถูกระงับจนกว่าจะมีการยกเลิกการหยุดชั่วคราวของหน้าเว็บ ซึ่งอาจ ไม่มีวันเกิดขึ้น (เช่น หากมีการทิ้งหน้าเว็บ) ซึ่งหมายความว่าเมื่อหน้าเว็บเปลี่ยนจากซ่อนเป็นหยุดชั่วคราว คุณต้องหยุดตัวจับเวลาหรือปิดการเชื่อมต่อใดๆ ที่หากหยุดชั่วคราวแล้ว อาจส่งผลต่อแท็บอื่นๆ ที่เปิดอยู่ในต้นทางเดียวกัน หรือส่งผลต่อ ความสามารถของเบราว์เซอร์ในการใส่หน้าเว็บลงใน แคชย้อนกลับ/ไปข้างหน้า โดยเฉพาะอย่างยิ่ง คุณควรดำเนินการต่อไปนี้
นอกจากนี้ คุณควรบันทึกสถานะมุมมองแบบไดนามิก (เช่น ตำแหน่งการเลื่อน
ในมุมมองรายการที่เลื่อนได้ไม่รู้จบ) ไว้ใน
หากหน้าเว็บเปลี่ยนจากหยุดทำงานกลับไปเป็นซ่อน คุณจะเปิดการเชื่อมต่อที่ปิดไปแล้วอีกครั้งหรือรีสตาร์ทการสำรวจที่คุณ หยุดไว้เมื่อหน้าเว็บหยุดทำงานในตอนแรกได้ |
Terminated |
โดยทั่วไปแล้ว คุณไม่จำเป็นต้องดำเนินการใดๆ เมื่อหน้าเว็บเปลี่ยนไปเป็นสถานะสิ้นสุด เนื่องจากหน้าเว็บที่ถูกยกเลิกการโหลดอันเป็นผลมาจากการกระทําของผู้ใช้จะเข้าสู่สถานะซ่อนก่อนเสมอ ที่จะเข้าสู่สถานะสิ้นสุด จึงควรดําเนินตรรกะการสิ้นสุดเซสชัน (เช่น การคงสถานะแอปพลิเคชันและการรายงานไปยัง Analytics) ในสถานะซ่อน นอกจากนี้ (ดังที่กล่าวไว้ในคำแนะนำสำหรับสถานะซ่อน) นักพัฒนาแอปควรทราบว่าในหลายกรณี (โดยเฉพาะบนอุปกรณ์เคลื่อนที่) ระบบไม่สามารถตรวจหาการเปลี่ยนไปใช้สถานะสิ้นสุดได้อย่างน่าเชื่อถือ ดังนั้นนักพัฒนาแอปที่ต้องพึ่งพาเหตุการณ์การสิ้นสุด (เช่น |
Discarded |
นักพัฒนาแอปไม่สามารถสังเกตสถานะทิ้งได้ในขณะที่ระบบทิ้งหน้าเว็บ เนื่องจากโดยปกติแล้วระบบจะทิ้งหน้าเว็บเมื่อมีข้อจำกัดด้านทรัพยากร และการยกเลิกการตรึงหน้าเว็บเพียงเพื่อให้สคริปต์ทำงานเพื่อตอบสนองต่อเหตุการณ์การทิ้งนั้นเป็นไปไม่ได้ในกรณีส่วนใหญ่ ด้วยเหตุนี้ คุณจึงควรเตรียมพร้อมรับมือกับความเป็นไปได้ที่จะมีการทิ้ง
การเปลี่ยนแปลงจากซ่อนเป็นหยุด จากนั้นคุณจะ
ตอบสนองต่อการกู้คืนหน้าที่ถูกทิ้งเมื่อโหลดหน้าเว็บได้โดย
การตรวจสอบ |
อีกครั้งที่เนื่องจากความน่าเชื่อถือและการจัดลำดับเหตุการณ์วงจรของหน้าเว็บไม่ได้ นำไปใช้ในเบราว์เซอร์ทั้งหมดอย่างสม่ำเสมอ วิธีที่ง่ายที่สุดในการทำตามคำแนะนำ ในตารางคือการใช้ PageLifecycle.js
API วงจรเดิมที่ควรหลีกเลี่ยง
ควรหลีกเลี่ยงเหตุการณ์ต่อไปนี้ทุกครั้งที่ทำได้
เหตุการณ์การยกเลิกการโหลด
นักพัฒนาแอปจํานวนมากถือว่าเหตุการณ์ unload เป็นการเรียกกลับที่รับประกันและใช้เป็น
สัญญาณสิ้นสุดเซสชันเพื่อบันทึกสถานะและส่งข้อมูลวิเคราะห์ แต่การทําเช่นนี้ไม่น่าเชื่อถืออย่างยิ่ง โดยเฉพาะในอุปกรณ์เคลื่อนที่ unload เหตุการณ์จะไม่
ทริกเกอร์ในสถานการณ์การเลิกโหลดทั่วไปหลายอย่าง รวมถึงการปิดแท็บจากตัวสลับแท็บในอุปกรณ์เคลื่อนที่หรือการปิดแอปเบราว์เซอร์จากตัวสลับแอป
ด้วยเหตุนี้ คุณจึงควรใช้เหตุการณ์ visibilitychange เพื่อพิจารณาว่าเซสชันสิ้นสุดเมื่อใด และพิจารณาสถานะที่ซ่อนเป็นเวลาที่เชื่อถือได้ล่าสุดในการบันทึกข้อมูลแอปและผู้ใช้
นอกจากนี้ การมีตัวแฮนเดิลเหตุการณ์ unload ที่ลงทะเบียน (ผ่าน onunload หรือ addEventListener()) เพียงอย่างเดียวอาจทำให้เบราว์เซอร์ไม่สามารถจัดเก็บหน้าเว็บไว้ใน Back-Forward Cache เพื่อให้โหลดหน้าเว็บก่อนหน้าและถัดไปได้เร็วขึ้น
ในเบราว์เซอร์ที่ทันสมัยทั้งหมด เราขอแนะนําให้ใช้เหตุการณ์
pagehide เสมอเพื่อตรวจหาการยกเลิกการโหลดหน้าเว็บที่อาจเกิดขึ้น (หรือที่เรียกว่าสถานะสิ้นสุด) แทนเหตุการณ์ unload หากคุณต้องรองรับ Internet Explorer เวอร์ชัน 10 และต่ำกว่า คุณควรตรวจหาเหตุการณ์ pagehide และใช้ unload เฉพาะในกรณีที่เบราว์เซอร์ไม่รองรับ pagehide
const terminationEvent = 'onpagehide' in self ? 'pagehide' : 'unload';
window.addEventListener(terminationEvent, (event) => {
// Note: if the browser is able to cache the page, `event.persisted`
// is `true`, and the state is frozen rather than terminated.
});
เหตุการณ์ beforeunload
เหตุการณ์ beforeunload มีปัญหาคล้ายกับเหตุการณ์ unload กล่าวคือ ในอดีต การมีเหตุการณ์ beforeunload อาจทำให้หน้าเว็บไม่มีสิทธิ์ใช้ Back-Forward Cache เบราว์เซอร์ที่ทันสมัย
ไม่มีข้อจำกัดนี้ แม้ว่าเบราว์เซอร์บางตัวจะไม่ทริกเกอร์เหตุการณ์ beforeunload เมื่อพยายามใส่หน้าเว็บลงในแคชย้อนกลับ/ไปข้างหน้าเพื่อเป็นการป้องกัน ซึ่งหมายความว่าเหตุการณ์นี้ไม่น่าเชื่อถือในฐานะสัญญาณสิ้นสุดเซสชัน
นอกจากนี้ เบราว์เซอร์บางตัว (รวมถึง Chrome)
กำหนดให้ผู้ใช้ต้องโต้ตอบในหน้าเว็บก่อนจึงจะอนุญาตให้ทริกเกอร์เหตุการณ์ beforeunload ได้
ซึ่งส่งผลต่อความน่าเชื่อถือของเหตุการณ์ดังกล่าว
ความแตกต่างอย่างหนึ่งระหว่าง beforeunload กับ unload คือ beforeunload มีการใช้งานที่ถูกต้องตามกฎหมาย เช่น เมื่อคุณต้องการเตือนผู้ใช้
ว่ามีการเปลี่ยนแปลงที่ยังไม่ได้บันทึกซึ่งจะหายไปหากผู้ใช้ยังคงยกเลิกการโหลดหน้าเว็บ
เนื่องจากมีเหตุผลที่สมควรในการใช้ beforeunload เราจึงขอแนะนำให้คุณ
เฉพาะเพิ่ม Listener ของ beforeunload เมื่อผู้ใช้มีการเปลี่ยนแปลงที่ยังไม่ได้บันทึก แล้ว
นำออกทันทีหลังจากบันทึกแล้ว
กล่าวคือ อย่าทำเช่นนี้ (เนื่องจากจะเพิ่มbeforeunload Listener
โดยไม่มีเงื่อนไข)
addEventListener('beforeunload', (event) => {
// A function that returns `true` if the page has unsaved changes.
if (pageHasUnsavedChanges()) {
event.preventDefault();
// Legacy support for older browsers.
event.returnValue = true;
}
});
ให้ทำดังนี้แทน (เนื่องจากจะเพิ่ม Listener beforeunload เฉพาะเมื่อจำเป็น
และนำออกเมื่อไม่จำเป็น)
const beforeUnloadListener = (event) => {
event.preventDefault();
// Legacy support for older browsers.
event.returnValue = true;
};
// A function that adds a `beforeunload` listener if there are unsaved changes.
onPageHasUnsavedChanges(() => {
addEventListener('beforeunload', beforeUnloadListener);
});
// A function that removes the `beforeunload` listener when the page's unsaved
// changes are resolved.
onAllChangesSaved(() => {
removeEventListener('beforeunload', beforeUnloadListener);
});
คำถามที่พบบ่อย
ทำไมจึงไม่มีสถานะ "กำลังโหลด"
Page Lifecycle API กำหนดสถานะให้แยกกันและไม่ซ้ำกัน เนื่องจากหน้าเว็บสามารถโหลดในสถานะใช้งานอยู่ ไม่ใช้งาน หรือซ่อนอยู่ และ เนื่องจากหน้าเว็บสามารถเปลี่ยนสถานะหรือแม้แต่ถูกปิดก่อนที่จะโหลดเสร็จสมบูรณ์ สถานะการโหลดแยกต่างหากจึงไม่สมเหตุสมผลภายในกระบวนทัศน์นี้
หน้าเว็บของฉันทำงานสำคัญเมื่อซ่อนอยู่ ฉันจะหยุดไม่ให้ระบบหยุดชั่วคราวหรือทิ้งหน้าเว็บได้อย่างไร
มีเหตุผลที่ถูกต้องหลายประการที่ทำให้หน้าเว็บไม่ควรถูกระงับขณะทำงานในสถานะซ่อน ตัวอย่างที่ชัดเจนที่สุดคือแอปที่เล่นเพลง
นอกจากนี้ ยังมีกรณีที่ Chrome ไม่ควรทิ้งหน้าเว็บเนื่องจากอาจเป็นอันตราย เช่น หากหน้าเว็บมีแบบฟอร์มที่มีข้อมูลที่ผู้ใช้ป้อนแต่ยังไม่ได้ส่ง หรือหากมีbeforeunloadแฮนเดิลที่แจ้งเตือนเมื่อหน้าเว็บกำลังเลิกโหลด
ในตอนนี้ Chrome จะระมัดระวังในการทิ้งหน้าเว็บและจะทำก็ต่อเมื่อมั่นใจว่าจะไม่ส่งผลกระทบต่อผู้ใช้ ตัวอย่างเช่น หน้าเว็บที่สังเกตได้ว่าทำสิ่งต่อไปนี้ขณะอยู่ในสถานะซ่อนจะไม่ถูกทิ้ง เว้นแต่จะอยู่ภายใต้ข้อจำกัดด้านทรัพยากรที่รุนแรง
- การเล่นเสียง
- การใช้ WebRTC
- การอัปเดตชื่อตารางหรือ favicon
- การแสดงการแจ้งเตือน
- การส่งข้อความ Push
ดูรายการฟีเจอร์ปัจจุบันที่ใช้ในการพิจารณาว่าแท็บจะหยุดทำงานหรือทิ้งได้อย่างปลอดภัยหรือไม่ได้ที่ฮิวริสติกสำหรับการหยุดทำงานและการทิ้ง ใน Chrome
แคชย้อนหลังเป็นคำที่ใช้อธิบาย การเพิ่มประสิทธิภาพการนำทางที่เบราว์เซอร์บางรายการนำมาใช้เพื่อให้การใช้ปุ่มย้อนกลับและ ไปข้างหน้าเร็วขึ้น
เมื่อผู้ใช้ออกจากหน้าเว็บ เบราว์เซอร์เหล่านี้จะตรึงเวอร์ชันของหน้าเว็บนั้นไว้เพื่อให้กลับมาใช้งานต่อได้อย่างรวดเร็วในกรณีที่ผู้ใช้กลับมาโดยใช้ปุ่มย้อนกลับหรือไปข้างหน้า โปรดทราบว่าการเพิ่มunload
ตัวแฮนเดิลเหตุการณ์จะป้องกันไม่ให้เพิ่มประสิทธิภาพได้
การหยุดทำงานนี้มีฟังก์ชันการทำงานเหมือนกับ การหยุดทำงานของเบราว์เซอร์เพื่อประหยัด CPU/แบตเตอรี่ทุกประการ ด้วยเหตุนี้ จึงถือเป็นส่วนหนึ่งของสถานะวงจรหยุดทำงาน
หากฉันเรียกใช้ API แบบไม่พร้อมกันในสถานะหยุดทำงานหรือสิ้นสุดไม่ได้ ฉันจะบันทึกข้อมูลลงใน IndexedDB ได้อย่างไร
ในสถานะหยุดทำงานและสิ้นสุด งานที่หยุดทำงานได้ ในคิวงานของหน้าเว็บ จะถูกระงับ ซึ่งหมายความว่า API แบบไม่พร้อมกันและ API ที่อิงตามการเรียกกลับจะใช้ได้อย่างไม่น่าเชื่อถือ
แม้ว่า API ของ IndexedDB ส่วนใหญ่จะอิงตามการเรียกกลับ แต่เมธอด
commit()
ในอินเทอร์เฟซ
IDBTransaction
จะช่วยให้เริ่มกระบวนการคอมมิตใน
ธุรกรรมที่ใช้งานอยู่ได้โดยไม่ต้องรอให้ระบบส่งเหตุการณ์จากคำขอที่รอดำเนินการ
ซึ่งเป็นวิธีที่เชื่อถือได้ในการบันทึกข้อมูลลงในฐานข้อมูล IndexedDB ในเครื่องมือฟังเหตุการณ์ freeze หรือ visibilitychange เนื่องจากระบบจะเรียกใช้การคอมมิตทันทีแทนที่จะจัดคิวไว้ในงานแยกต่างหาก
การทดสอบแอปในสถานะหยุดทำงานและสถานะทิ้ง
หากต้องการทดสอบลักษณะการทำงานของแอปในสถานะหยุดทำงานและสถานะทิ้ง คุณสามารถไปที่
chrome://discards เพื่อหยุดทำงานหรือทิ้งแท็บที่เปิดอยู่
ซึ่งจะช่วยให้คุณมั่นใจได้ว่าหน้าเว็บจะจัดการเหตุการณ์ freeze และ resume
รวมถึงแฟล็ก document.wasDiscarded อย่างถูกต้องเมื่อโหลดหน้าเว็บซ้ำหลังจาก
ทิ้ง
สรุป
นักพัฒนาแอปที่ต้องการเคารพทรัพยากรระบบของอุปกรณ์ผู้ใช้ ควรสร้างแอปโดยคำนึงถึงสถานะวงจรหน้าเว็บ สิ่งสำคัญคือหน้าเว็บต้องไม่ใช้ทรัพยากรระบบมากเกินไปในสถานการณ์ที่ผู้ใช้ไม่คาดคิด
ยิ่งนักพัฒนาแอปเริ่มใช้ Page Lifecycle API ใหม่มากเท่าใด เบราว์เซอร์ก็จะยิ่งปลอดภัยมากขึ้นเท่านั้น ในการระงับและทิ้งหน้าเว็บที่ไม่ได้ใช้งาน ซึ่งหมายความว่าเบราว์เซอร์จะใช้หน่วยความจำ, CPU, แบตเตอรี่ และทรัพยากรเครือข่ายน้อยลง ซึ่งเป็นประโยชน์ต่อผู้ใช้