การรองรับเบราว์เซอร์
บางครั้งเบราว์เซอร์สมัยใหม่จะระงับหน้าเว็บหรือทิ้งหน้าเว็บทั้งหมดเมื่อทรัพยากรของระบบมีจํากัด ในอนาคต เบราว์เซอร์ต้องการดำเนินการนี้อย่างสม่ำเสมอเพื่อใช้พลังงานและหน่วยความจำน้อยลง Page Lifecycle API มีฮุกวงจรเพื่อให้หน้าเว็บจัดการการแทรกแซงของเบราว์เซอร์เหล่านี้ได้อย่างปลอดภัยโดยไม่ส่งผลกระทบต่อประสบการณ์ของผู้ใช้ ลองดู API เพื่อดูว่าคุณควรติดตั้งใช้งานฟีเจอร์เหล่านี้ในแอปพลิเคชันหรือไม่
ข้อมูลเบื้องต้น
วงจรของแอปพลิเคชันเป็นวิธีหลักที่ระบบปฏิบัติการสมัยใหม่จัดการทรัพยากร ใน Android, iOS และ Windows เวอร์ชันล่าสุด ระบบปฏิบัติการสามารถเริ่มและหยุดแอปได้ทุกเมื่อ ซึ่งช่วยให้แพลตฟอร์มเหล่านี้ปรับปรุงและจัดสรรทรัพยากรใหม่ในลักษณะที่เป็นประโยชน์ต่อผู้ใช้มากที่สุด
ที่ผ่านมาบนเว็บไม่มีวงจรดังกล่าวและแอปสามารถทำงานได้อย่างต่อเนื่อง เมื่อมีหน้าเว็บจำนวนมากทำงานอยู่ ทรัพยากรที่สำคัญของระบบ เช่น หน่วยความจำ, CPU, แบตเตอรี่ และเครือข่าย อาจมีการสมัครใช้บริการมากเกินไป ซึ่งส่งผลให้ผู้ใช้ปลายทางได้รับประสบการณ์การใช้งานที่ไม่ดี
แม้ว่าแพลตฟอร์มเว็บจะมีเหตุการณ์ที่เกี่ยวข้องกับสถานะวงจรการใช้งานมาอย่างยาวนานแล้ว เช่น load
,
unload
และ
visibilitychange
แต่เหตุการณ์เหล่านี้ช่วยให้นักพัฒนาแอปตอบสนองต่อการเปลี่ยนแปลงสถานะวงจรการใช้งานที่ผู้ใช้เป็นผู้เริ่มเท่านั้น เพื่อให้เว็บทำงานได้อย่างน่าเชื่อถือในอุปกรณ์ที่มีกำลังไฟต่ำ (และประหยัดทรัพยากรมากขึ้นโดยทั่วไปในแพลตฟอร์มทั้งหมด) เบราว์เซอร์ต้องมีวิธีเรียกคืนและจัดสรรทรัพยากรของระบบใหม่อย่างสม่ำเสมอ
อันที่จริงแล้ว เบราว์เซอร์ในปัจจุบันใช้มาตรการต่างๆ เพื่อประหยัดทรัพยากรสำหรับหน้าในแท็บที่ทำงานอยู่เบื้องหลังอยู่แล้ว และเบราว์เซอร์หลายตัว (โดยเฉพาะ Chrome) ต้องการที่จะใช้มาตรการเหล่านี้มากขึ้นเพื่อลดการใช้ทรัพยากรโดยรวม
ปัญหาคือนักพัฒนาแอปไม่มีวิธีเตรียมพร้อมรับการแทรกแซงประเภทนี้ที่ระบบเป็นผู้เริ่ม หรือแม้แต่ไม่รู้ตัวว่ากำลังเกิดขึ้น ซึ่งหมายความว่าเบราว์เซอร์ต้องระมัดระวังหรืออาจทำให้หน้าเว็บใช้งานไม่ได้
Page Lifecycle API จะพยายามแก้ปัญหานี้ด้วยวิธีต่อไปนี้
- การนําเสนอและกำหนดมาตรฐานแนวคิดสถานะวงจรบนเว็บ
- การกําหนดสถานะใหม่ซึ่งระบบเริ่มต้นขึ้น ซึ่งช่วยให้เบราว์เซอร์จํากัดทรัพยากรที่แท็บที่ซ่อนอยู่หรือไม่ใช้งานสามารถใช้ได้
- การสร้าง API และเหตุการณ์ใหม่ที่ช่วยให้นักพัฒนาเว็บตอบสนองต่อการเปลี่ยนจากสถานะหนึ่งไปยังอีกสถานะหนึ่งซึ่งระบบเป็นผู้เริ่ม
โซลูชันนี้ช่วยให้นักพัฒนาเว็บคาดการณ์ได้ว่าจะสร้างแอปพลิเคชันที่ทนต่อการแทรกแซงของระบบได้อย่างไร และช่วยให้เบราว์เซอร์เพิ่มประสิทธิภาพทรัพยากรของระบบได้มากขึ้น ซึ่งท้ายที่สุดแล้วจะเป็นประโยชน์ต่อผู้ใช้เว็บทุกคน
ส่วนที่เหลือของโพสต์นี้จะแนะนำฟีเจอร์ใหม่ของวงจรชีวิตของหน้าเว็บ และสำรวจความสัมพันธ์ของฟีเจอร์เหล่านี้กับสถานะและเหตุการณ์ทั้งหมดของแพลตฟอร์มเว็บที่มีอยู่ นอกจากนี้ ยังมีคําแนะนําและแนวทางปฏิบัติแนะนําสําหรับประเภทงานที่นักพัฒนาแอปควร (และไม่ควร) ทําในแต่ละรัฐ
ภาพรวมสถานะและเหตุการณ์ในวงจรของหน้าเว็บ
สถานะวงจรชีวิตของหน้าเว็บทั้งหมดจะแยกกันและแยกกันอยู่ กล่าวคือ หน้าเว็บจะอยู่ในสถานะใดสถานะหนึ่งได้เพียงสถานะเดียว และการเปลี่ยนแปลงส่วนใหญ่ในสถานะวงจรชีวิตของหน้าเว็บมักจะสังเกตได้ผ่านเหตุการณ์ DOM (ดูข้อยกเว้นในคําแนะนําสําหรับนักพัฒนาซอฟต์แวร์สําหรับสถานะแต่ละสถานะ)
วิธีอธิบายสถานะวงจรชีวิตของหน้าเว็บ รวมถึงเหตุการณ์ที่ส่งสัญญาณการเปลี่ยนสถานะระหว่างสถานะต่างๆ ได้ง่ายที่สุดคือการใช้แผนภาพ ดังนี้
รัฐ
ตารางต่อไปนี้จะอธิบายสถานะแต่ละรายการโดยละเอียด รวมถึงแสดงสถานะต่างๆ ที่เป็นไปได้ซึ่งอาจเกิดขึ้นก่อนและหลัง รวมถึงเหตุการณ์ที่นักพัฒนาแอปสามารถใช้เพื่อสังเกตการเปลี่ยนแปลง
รัฐ | คำอธิบาย |
---|---|
ใช้งานอยู่ |
หน้าเว็บจะอยู่ในสถานะใช้งานอยู่หากมองเห็นได้และมีโฟกัสอินพุต
สถานะก่อนหน้าที่เป็นไปได้: |
เชิงรับ |
หน้าเว็บจะอยู่ในสถานะไม่ได้ใช้งานหากมองเห็นได้และไม่มีโฟกัสการป้อนข้อมูล
สถานะก่อนหน้าที่เป็นไปได้:
สถานะถัดไปที่เป็นไปได้: |
ซ่อน |
หน้าเว็บจะอยู่ในสถานะซ่อนอยู่หากผู้ใช้ไม่เห็นหน้านั้น (และไม่ได้ถูกหยุด ทิ้ง หรือสิ้นสุดการทำงาน)
สถานะก่อนหน้าที่เป็นไปได้:
สถานะถัดไปที่เป็นไปได้: |
ค้าง |
ในสถานะหยุดทำงาน เบราว์เซอร์จะระงับการดำเนินการของงานที่คิวงานของหน้าเว็บจนกว่าหน้าเว็บจะเลิกหยุดทำงาน ซึ่งหมายความว่าตัวจับเวลา JavaScript และ Callback ของ Fetch จะไม่ทํางาน งานที่กำลังทำงานอยู่จะทำงานเสร็จ (ที่สำคัญที่สุดคือ
callbacks เบราว์เซอร์จะหยุดหน้าเว็บชั่วคราวเพื่อประหยัดการใช้ CPU/แบตเตอรี่/อินเทอร์เน็ต และยังช่วยในการ การไปยังหน้าก่อนหน้า/ถัดไปได้เร็วขึ้นด้วย ซึ่งทำให้ไม่ต้องโหลดหน้าเว็บใหม่ทั้งหมด
สถานะก่อนหน้าที่เป็นไปได้:
สถานะที่เป็นไปได้ถัดไป: |
สิ้นสุดแล้ว |
หน้าเว็บจะอยู่ในสถานะสิ้นสุดเมื่อเบราว์เซอร์เริ่มยกเลิกการโหลดและล้างหน้าเว็บออกจากหน่วยความจํา สถานะนี้จะเริ่ม งานใหม่ไม่ได้ และระบบอาจหยุดงานที่กำลังดำเนินการอยู่หากทำงานนานเกินไป
สถานะก่อนหน้าที่เป็นไปได้:
สถานะถัดไปที่เป็นไปได้: |
ทิ้งแล้ว |
หน้าเว็บจะอยู่ในสถานะถูกละทิ้งเมื่อเบราว์เซอร์ยกเลิกการโหลดหน้าเว็บเพื่อประหยัดทรัพยากร ไม่มีงาน เหตุการณ์ที่เรียกกลับ หรือ JavaScript ประเภทใดก็ตามที่ทำงานได้ในสถานะนี้ เนื่องจากโดยปกติแล้วการทิ้งจะเกิดขึ้นภายใต้ข้อจำกัดด้านทรัพยากร ซึ่งทำให้เริ่มกระบวนการใหม่ไม่ได้ ในสถานะทิ้ง ผู้ใช้มักจะเห็นแท็บนั้นเอง (รวมถึงชื่อแท็บและ Favicon) แม้ว่าหน้าเว็บจะหายไปแล้วก็ตาม
สถานะก่อนหน้าที่เป็นไปได้:
สถานะถัดไปที่เป็นไปได้: |
กิจกรรม
เบราว์เซอร์จะส่งเหตุการณ์จำนวนมาก แต่มีเพียงส่วนน้อยที่บ่งบอกถึงการเปลี่ยนแปลงที่เป็นไปได้ในสถานะวงจรชีวิตของหน้าเว็บ ตารางต่อไปนี้แสดงเหตุการณ์ทั้งหมดที่เกี่ยวข้องกับวงจรของลูกค้าและสถานะที่เหตุการณ์อาจเปลี่ยนจากสถานะหนึ่งไปยังอีกสถานะหนึ่ง
ชื่อ | รายละเอียด |
---|---|
focus
|
องค์ประกอบ DOM ได้รับโฟกัส
หมายเหตุ: เหตุการณ์
สถานะก่อนหน้าที่เป็นไปได้:
สถานะปัจจุบันที่เป็นไปได้: |
blur
|
องค์ประกอบ DOM เสียโฟกัส
หมายเหตุ: เหตุการณ์
สถานะก่อนหน้าที่เป็นไปได้:
สถานะปัจจุบันที่เป็นไปได้: |
visibilitychange
|
ค่า
|
freeze
*
|
หน้าเว็บเพิ่งหยุดทำงาน ระบบจะไม่เริ่มงานใดๆ ที่ หยุดชั่วคราวได้ในคิวงานของหน้า
สถานะก่อนหน้าที่เป็นไปได้:
สถานะปัจจุบันที่เป็นไปได้: |
resume
*
|
เบราว์เซอร์กลับมาแสดงหน้าเว็บที่ค้างอีกครั้ง
สถานะก่อนหน้าที่เป็นไปได้:
สถานะปัจจุบันที่เป็นไปได้: |
pageshow
|
กําลังไปยังรายการประวัติเซสชัน ซึ่งอาจเป็นการโหลดหน้าเว็บใหม่หรือหน้าเว็บที่มาจากแคชย้อนกลับ/ไปข้างหน้า หากหน้าเว็บมาจากแคชย้อนหลัง/ไปข้างหน้า พร็อพเพอร์ตี้
สถานะก่อนหน้าที่เป็นไปได้:
สถานะปัจจุบันที่เป็นไปได้: |
pagehide
|
รายการประวัติเซสชันที่กําลังสํารวจ หากผู้ใช้ไปยังหน้าอื่นและเบราว์เซอร์เพิ่มหน้าปัจจุบันลงในแคชย้อนกลับ/ไปข้างหน้าเพื่อใช้ซ้ำในภายหลังได้ พร็อพเพอร์ตี้
สถานะก่อนหน้าที่เป็นไปได้:
สถานะปัจจุบันที่เป็นไปได้: |
beforeunload
|
ระบบกำลังจะยกเลิกการโหลดหน้าต่าง เอกสาร และทรัพยากรของเอกสาร เอกสารจะยังคงปรากฏอยู่และยังยกเลิกกิจกรรมได้ในตอนนี้
สำคัญ: ควรใช้เหตุการณ์
สถานะก่อนหน้าที่เป็นไปได้:
สถานะปัจจุบันที่เป็นไปได้: |
unload
|
หน้าเว็บกำลังยกเลิกการโหลด
คำเตือน: เราขอแนะนำอย่างยิ่งว่าอย่าใช้เหตุการณ์
สถานะก่อนหน้าที่เป็นไปได้:
สถานะปัจจุบันที่เป็นไปได้: |
* บ่งบอกถึงเหตุการณ์ใหม่ที่ Page Lifecycle API กำหนด
ฟีเจอร์ใหม่ที่เพิ่มเข้ามาใน Chrome 68
แผนภูมิก่อนหน้าแสดงสถานะ 2 สถานะที่ระบบเป็นผู้เริ่ม ไม่ใช่ผู้ใช้ ได้แก่ หยุดชั่วคราวและทิ้ง ดังที่ได้กล่าวไว้ก่อนหน้านี้ เบราว์เซอร์ในปัจจุบันจะค้างและทิ้งแท็บที่ซ่อนอยู่เป็นครั้งคราว (ตามการพิจารณาของเบราว์เซอร์) แต่นักพัฒนาแอปจะไม่ทราบเวลาที่จะเกิดเหตุการณ์นี้
ใน 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';
};
ในทางกลับกัน สถานะหยุดทำงานและสิ้นสุดจะตรวจพบได้ใน Listener เหตุการณ์ที่เกี่ยวข้องเท่านั้น (freeze
และ pagehide
) เมื่อสถานะมีการเปลี่ยนแปลง
วิธีสังเกตการเปลี่ยนแปลงสถานะ
จากฟังก์ชัน 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()
- กำหนดฟังก์ชันที่ยอมรับสถานะถัดไป และหากมีการเปลี่ยนแปลง ระบบจะบันทึกการเปลี่ยนแปลงสถานะลงในคอนโซล
- เพิ่ม capturing Listener เหตุการณ์สําหรับเหตุการณ์วงจรชีวิตของจําเป็นทั้งหมด ซึ่งจะเรียก
logStateChange()
โดยส่งสถานะถัดไป
สิ่งที่ควรทราบเกี่ยวกับโค้ดนี้คือระบบจะเพิ่ม Listener เหตุการณ์ทั้งหมดลงใน window
และ Listener ทั้งหมดจะส่งผ่าน {capture: true}
ซึ่งอาจเป็นเพราะสาเหตุต่อไปนี้
- เหตุการณ์วงจรชีวิตของหน้าเว็บบางรายการอาจมีเป้าหมายเดียวกัน
pagehide
และpageshow
จะทํางานในwindow
,visibilitychange
,freeze
และresume
จะทํางานในdocument
และfocus
และblur
จะทํางานในองค์ประกอบ DOM ที่เกี่ยวข้อง - เหตุการณ์เหล่านี้ส่วนใหญ่จะไม่ทําให้เกิดเหตุการณ์ย่อย ซึ่งหมายความว่าคุณจะเพิ่ม Listener เหตุการณ์แบบไม่จับไปยังองค์ประกอบบรรพบุรุษทั่วไปและสังเกตเหตุการณ์ทั้งหมดไม่ได้
- ระยะการบันทึกจะทำงานก่อนระยะเป้าหมายหรือระยะบับเบิล ดังนั้นการเพิ่ม Listeners ในส่วนนี้จะช่วยให้มั่นใจได้ว่า Listeners จะทำงานก่อนที่โค้ดอื่นๆ จะยกเลิกได้
คําแนะนําของนักพัฒนาแอปสําหรับแต่ละสถานะ
ในฐานะนักพัฒนาซอฟต์แวร์ คุณต้องเข้าใจทั้งสถานะวงจรชีวิตของหน้าเว็บและวิธีสังเกตสถานะเหล่านั้นในโค้ด เนื่องจากประเภทงานที่ควร (และไม่ควร) ทํานั้นขึ้นอยู่กับสถานะของหน้าเว็บเป็นส่วนใหญ่
ตัวอย่างเช่น การแสดงการแจ้งเตือนชั่วคราวต่อผู้ใช้หากหน้าเว็บอยู่ในสถานะซ่อนอยู่นั้นไม่สมเหตุสมผล แม้ว่าตัวอย่างนี้จะค่อนข้างชัดเจน แต่ก็มีคําแนะนําอื่นๆ ที่ไม่ค่อยชัดเจนนักซึ่งควรกล่าวถึง
รัฐ | คําแนะนําสําหรับนักพัฒนาแอป |
---|---|
Active |
สถานะใช้งานอยู่เป็นช่วงเวลาสําคัญที่สุดสําหรับผู้ใช้ และเป็นช่วงเวลาที่สําคัญที่สุดที่หน้าเว็บต้อง ตอบสนองต่ออินพุตของผู้ใช้ งานที่ไม่เกี่ยวข้องกับ UI ซึ่งอาจบล็อกเทรดหลักควรลดลำดับความสำคัญเป็น ช่วงเวลาที่ไม่มีการใช้งานหรือ ส่งไปยัง Web Worker |
Passive |
ในสถานะไม่โต้ตอบ ผู้ใช้ไม่ได้โต้ตอบกับหน้าเว็บ แต่ยังคงมองเห็นหน้าเว็บได้ ซึ่งหมายความว่าการอัปเดต UI และภาพเคลื่อนไหวจะยังคงราบรื่น แต่เวลาของการอัปเดตเหล่านี้จะสำคัญน้อยลง เมื่อหน้าเว็บเปลี่ยนจากใช้งานอยู่เป็นไม่ได้ใช้งาน แสดงว่าถึงเวลาที่จะเก็บสถานะแอปพลิเคชันที่ไม่ได้บันทึกไว้ |
เมื่อหน้าเว็บเปลี่ยนจากไม่ได้แสดงเป็นซ่อนอยู่ เป็นไปได้ว่าผู้ใช้จะไม่โต้ตอบกับหน้าเว็บนั้นอีกจนกว่าจะโหลดซ้ำ นอกจากนี้ การเปลี่ยนเป็นซ่อนอยู่มักเป็นการเปลี่ยนแปลงสถานะสุดท้ายที่นักพัฒนาแอปสังเกตได้ (โดยเฉพาะอย่างยิ่งในอุปกรณ์เคลื่อนที่ เนื่องจากผู้ใช้สามารถปิดแท็บหรือแอปเบราว์เซอร์เองได้ และเหตุการณ์ ซึ่งหมายความว่าคุณควรถือว่าสถานะซ่อนอยู่เป็นสถานะสิ้นสุดเซสชันของผู้ใช้ กล่าวคือ เก็บสถานะแอปพลิเคชันที่ไม่ได้บันทึกไว้และส่งข้อมูลวิเคราะห์ที่ไม่ได้ส่ง นอกจากนี้ คุณควรหยุดการอัปเดต UI (เนื่องจากผู้ใช้จะไม่เห็น) และควรหยุดงานทั้งหมดที่ผู้ใช้ไม่ต้องการให้ทำงานอยู่เบื้องหลัง |
|
Frozen |
ในสถานะหยุดทำงาน ระบบจะระงับ งานที่สามารถหยุดทำงานได้ใน คิวงานจนกว่าหน้าเว็บจะเลิกหยุดทำงาน ซึ่งอาจไม่เกิดขึ้นเลย (เช่น หากมีการทิ้งหน้าเว็บ) ซึ่งหมายความว่าเมื่อหน้าเว็บเปลี่ยนจากซ่อนอยู่เป็นหยุดทำงาน คุณจะต้องหยุดตัวจับเวลาหรือยกเลิกการเชื่อมต่อทั้งหมด ซึ่งหากหยุดทำงานอาจส่งผลต่อแท็บอื่นๆ ที่เปิดอยู่ในต้นทางเดียวกัน หรือส่งผลต่อความสามารถของเบราว์เซอร์ในการวางหน้าเว็บใน แคชย้อนกลับ/ไปข้างหน้า โดยเฉพาะอย่างยิ่ง คุณต้องทำสิ่งต่อไปนี้
นอกจากนี้ คุณควรเก็บสถานะมุมมองแบบไดนามิก (เช่น ตำแหน่งการเลื่อนในมุมมองรายการแบบไม่สิ้นสุด) ไว้ใน
หากหน้าเว็บเปลี่ยนจากค้างกลับไปเป็นซ่อนอยู่ คุณจะเปิดการเชื่อมต่อที่ปิดอยู่อีกครั้งหรือเริ่มการสำรวจอีกครั้งได้ ซึ่งคุณหยุดไว้เมื่อหน้าเว็บค้างครั้งแรก |
Terminated |
โดยทั่วไปแล้ว คุณไม่จำเป็นต้องดำเนินการใดๆ เมื่อหน้าเว็บเปลี่ยนเป็นสถานะสิ้นสุด เนื่องจากหน้าเว็บที่ยกเลิกการโหลดอันเป็นผลมาจากการกระทำของผู้ใช้จะผ่านสถานะซ่อนอยู่เสมอก่อนที่จะเข้าสู่สถานะสิ้นสุด สถานะซ่อนอยู่จึงเป็นสถานะที่ควรใช้ตรรกะการสิ้นสุดเซสชัน (เช่น สถานะแอปพลิเคชันที่เก็บไว้และการรายงานไปยัง Analytics) นอกจากนี้ (ตามที่ระบุไว้ในคําแนะนําสําหรับสถานะซ่อนอยู่) นักพัฒนาแอปจําเป็นต้องทราบว่าในหลายกรณี (โดยเฉพาะในอุปกรณ์เคลื่อนที่) การตรวจหาการเปลี่ยนเป็นสถานะสิ้นสุดนั้นไม่น่าเชื่อถือ ดังนั้นนักพัฒนาแอปที่พึ่งพาเหตุการณ์สิ้นสุด (เช่น |
Discarded |
นักพัฒนาแอปจะไม่เห็นสถานะทิ้งในขณะที่ระบบทิ้งหน้าเว็บ เนื่องจากโดยทั่วไปแล้ว ระบบจะทิ้งหน้าเว็บเนื่องจากข้อจํากัดด้านทรัพยากร และการเลิกตรึงหน้าเว็บเพียงเพื่อให้สคริปต์ทํางานเพื่อตอบสนองต่อเหตุการณ์การทิ้งนั้นทําไม่ได้ในเกือบทุกกรณี คุณจึงควรเตรียมพร้อมสำหรับกรณีที่ระบบจะทิ้งการเปลี่ยนแปลงจากซ่อนเป็นหยุดชั่วคราว จากนั้นคุณจะตอบสนองต่อการกู้คืนหน้าที่ทิ้งไปในเวลาที่หน้าเว็บโหลดได้โดยการตรวจสอบ |
เราขอย้ำอีกครั้งว่าความน่าเชื่อถือและลําดับเหตุการณ์ในวงจรของเบราว์เซอร์แต่ละรุ่นนั้นไม่สอดคล้องกัน ดังนั้นวิธีง่ายที่สุดในการทําตามคําแนะนําในตารางคือการใช้ PageLifecycle.js
API วงจรของลูกค้าเดิมที่ควรหลีกเลี่ยง
คุณควรหลีกเลี่ยงเหตุการณ์ต่อไปนี้หากเป็นไปได้
เหตุการณ์การยกเลิกการโหลด
นักพัฒนาซอฟต์แวร์จํานวนมากถือว่าเหตุการณ์ unload
เป็นคอลแบ็กที่รับประกันและใช้เป็นสัญญาณสิ้นสุดเซสชันเพื่อบันทึกสถานะและส่งข้อมูลวิเคราะห์ แต่วิธีนี้ไม่น่าเชื่อถืออย่างยิ่ง โดยเฉพาะในอุปกรณ์เคลื่อนที่ เหตุการณ์ unload
จะไม่ทริกเกอร์ในสถานการณ์การยกเลิกการโหลดทั่วไปหลายกรณี เช่น การปิดแท็บจากตัวสลับแท็บบนอุปกรณ์เคลื่อนที่ หรือการปิดแอปเบราว์เซอร์จากตัวสลับแอป
ด้วยเหตุนี้ คุณจึงควรใช้เหตุการณ์ visibilitychange
เพื่อระบุเวลาที่เซสชันสิ้นสุดลง และพิจารณาสถานะซ่อนเป็นเวลาที่เชื่อถือได้ล่าสุดในการบันทึกข้อมูลแอปและผู้ใช้
นอกจากนี้ การมีตัวจัดการเหตุการณ์ unload
ที่ลงทะเบียนไว้ (ผ่าน onunload
หรือ addEventListener()
) เพียงอย่างเดียวอาจทำให้เบราว์เซอร์ไม่สามารถจัดเก็บหน้าเว็บไว้ใน แคชย้อนกลับ/ไปข้างหน้า เพื่อให้โหลดหน้าเว็บย้อนกลับและไปข้างหน้าได้เร็วขึ้น
ในเบราว์เซอร์สมัยใหม่ทั้งหมด เราขอแนะนำให้ใช้เหตุการณ์ 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
เราจึงขอแนะนำให้คุณเพิ่มผู้ฟัง 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.
return (event.returnValue = true);
}
});
ให้ทำดังนี้ (เนื่องจากจะเพิ่ม beforeunload
listener เฉพาะเมื่อจำเป็น และนำออกเมื่อไม่จำเป็น)
const beforeUnloadListener = (event) => {
event.preventDefault();
// Legacy support for older browsers.
return (event.returnValue = true);
};
// A function that invokes a callback when the page has unsaved changes.
onPageHasUnsavedChanges(() => {
addEventListener('beforeunload', beforeUnloadListener);
});
// A function that invokes a callback when the page's unsaved changes are resolved.
onAllChangesSaved(() => {
removeEventListener('beforeunload', beforeUnloadListener);
});
คำถามที่พบบ่อย
ทำไมจึงไม่มีสถานะ "กำลังโหลด"
Page Lifecycle API กําหนดสถานะให้แยกกันอยู่และไม่สามารถเกิดขึ้นพร้อมกันได้ เนื่องจากหน้าเว็บสามารถโหลดได้ในสถานะใช้งานอยู่ ไม่ได้ใช้งาน หรือซ่อนอยู่ และเนื่องจากหน้าเว็บสามารถเปลี่ยนสถานะหรือหยุดทำงานก่อนที่จะโหลดเสร็จสมบูรณ์ สถานะการโหลดแยกต่างหากจึงไม่มีความหมายในแพราไดม์นี้
หน้าเว็บของฉันทํางานสําคัญเมื่อซ่อนอยู่ ฉันจะหยุดไม่ให้หน้าเว็บถูกหยุดทำงานหรือถูกทิ้งได้อย่างไร
มีเหตุผลที่ถูกต้องมากมายที่หน้าเว็บไม่ควรค้างขณะทำงานในสถานะซ่อนอยู่ ตัวอย่างที่ชัดเจนที่สุดคือแอปที่เล่นเพลง
นอกจากนี้ ยังมีกรณีที่ Chrome ไม่ควรทิ้งหน้าเว็บ เช่น หากหน้าเว็บมีแบบฟอร์มที่มีข้อมูลที่ผู้ใช้ป้อนไว้แต่ยังไม่ได้ส่ง หรือมีbeforeunload
แฮนเดิลที่เตือนเมื่อมีการยกเลิกการโหลดหน้าเว็บ
ขณะนี้ Chrome จะระมัดระวังในการทิ้งหน้าเว็บ และจะทิ้งก็ต่อเมื่อมั่นใจว่าจะไม่ส่งผลกระทบต่อผู้ใช้ ตัวอย่างเช่น ระบบจะไม่ทิ้งหน้าเว็บที่ตรวจพบว่าทําสิ่งต่อไปนี้ขณะอยู่ในสถานะซ่อน เว้นแต่จะมีข้อจํากัดด้านทรัพยากรอย่างรุนแรง
- กำลังเล่นเสียง
- การใช้ WebRTC
- การอัปเดตชื่อตารางหรือ Favicon
- การแสดงการแจ้งเตือน
- การส่งข้อความ Push
ดูฟีเจอร์รายการปัจจุบันที่ใช้เพื่อระบุว่าสามารถตรึงหรือทิ้งแท็บได้อย่างปลอดภัยหรือไม่ได้ที่วิธีการหาค่าประมาณสำหรับการตรึงและการทิ้งใน Chrome
แคชย้อนกลับ/ไปข้างหน้าเป็นคำที่ใช้อธิบายการเพิ่มประสิทธิภาพการไปยังส่วนต่างๆ ที่บางเบราว์เซอร์นำมาใช้ ซึ่งทำให้การใช้ปุ่มย้อนกลับและไปข้างหน้าเร็วขึ้น
เมื่อผู้ใช้ไปยังหน้าอื่น เบราว์เซอร์เหล่านี้จะหยุดหน้าเว็บเวอร์ชันนั้นไว้ชั่วคราวเพื่อให้กลับมาใช้งานได้อย่างรวดเร็วในกรณีที่ผู้ใช้ไปยังหน้าก่อนหน้าโดยใช้ปุ่มย้อนกลับหรือไปข้างหน้า โปรดทราบว่าการเพิ่ม unload
event handler จะทําให้การเพิ่มประสิทธิภาพนี้ใช้งานไม่ได้
ในแง่ความตั้งใจและวัตถุประสงค์ทั้งหมด การหยุดทำงานนี้ทำงานเหมือนกับการหยุดทำงานของเบราว์เซอร์เพื่อประหยัด CPU/แบตเตอรี่ ด้วยเหตุนี้จึงถือว่าเป็นส่วนหนึ่งของสถานะวงจร frozen
หากเรียกใช้ API แบบไม่สอดคล้องกันในสถานะหยุดทำงานหรือหยุดทำงานไปแล้ว ฉันจะบันทึกข้อมูลไปยัง IndexedDB ได้อย่างไร
ในสถานะหยุดทำงานและสิ้นสุดการทำงาน ระบบจะระงับงานที่หยุดทำงานได้ในคิวงานของหน้า ซึ่งหมายความว่าคุณจะใช้ API แบบแอซิงโครนัสและแบบใช้การเรียกกลับ เช่น IndexedDB ได้อย่างไม่เสถียร
ในอนาคต เราจะเพิ่มเมธอด commit()
ไปยังออบเจ็กต์ IDBTransaction
ซึ่งจะช่วยให้นักพัฒนาแอปมีวิธีดำเนินการธุรกรรมแบบเขียนอย่างเดียวที่มีประสิทธิภาพซึ่งไม่จําเป็นต้องใช้การเรียกกลับ กล่าวคือ หากนักพัฒนาแอปเขียนข้อมูลไปยัง IndexedDB เท่านั้นและไม่ได้ดำเนินการธุรกรรมที่ซับซ้อนซึ่งประกอบด้วยการอ่านและการเขียน เมธอด commit()
จะทำงานเสร็จก่อนที่คิวงานจะถูกระงับ (สมมติว่าฐานข้อมูล IndexedDB เปิดอยู่)
อย่างไรก็ตาม สําหรับโค้ดที่ต้องใช้ในปัจจุบัน นักพัฒนาแอปจะมี 2 ตัวเลือก ได้แก่
- ใช้พื้นที่เก็บข้อมูลของเซสชัน: พื้นที่เก็บข้อมูลของเซสชัน ทำงานแบบซิงค์และจะยังคงอยู่เมื่อมีการทิ้งหน้าเว็บ
- ใช้ IndexedDB จาก Service Worker: Service Worker สามารถจัดเก็บข้อมูลใน IndexedDB หลังจากที่หน้าเว็บสิ้นสุดหรือถูกทิ้ง ใน
freeze
หรือ ฟังก์ชันการเรียกเหตุการณ์pagehide
คุณสามารถส่งข้อมูลไปยัง Service Worker ผ่านpostMessage()
และ Service Worker จะจัดการการบันทึกข้อมูลได้
การทดสอบแอปในสถานะหยุดทำงานและถูกทิ้ง
หากต้องการทดสอบลักษณะการทำงานของแอปในสถานะหยุดทำงานและถูกทิ้ง ให้ไปที่ chrome://discards
เพื่อหยุดทำงานหรือทิ้งแท็บที่เปิดอยู่
วิธีนี้ช่วยให้มั่นใจได้ว่าหน้าเว็บจะจัดการเหตุการณ์ freeze
และ resume
รวมถึง Flag document.wasDiscarded
อย่างถูกต้องเมื่อมีการโหลดหน้าเว็บอีกครั้งหลังจากการทิ้ง
สรุป
นักพัฒนาแอปที่ต้องการเคารพทรัพยากรระบบของอุปกรณ์ของผู้ใช้ควรสร้างแอปโดยคำนึงถึงสถานะวงจรชีวิตของหน้าเว็บ หน้าเว็บไม่ควรใช้ทรัพยากรระบบมากเกินไปในสถานการณ์ที่ผู้ใช้ไม่คาดคิด
ยิ่งนักพัฒนาแอปเริ่มใช้ Page Lifecycle API ใหม่มากเท่าใด เบราว์เซอร์ก็จะยิ่งหยุดหน้าเว็บชั่วคราวและทิ้งหน้าเว็บที่ไม่ได้ใช้งานได้อย่างปลอดภัยมากขึ้นเท่านั้น ซึ่งหมายความว่าเบราว์เซอร์จะใช้หน่วยความจำ, CPU, แบตเตอรี่ และทรัพยากรเครือข่ายน้อยลง ซึ่งถือเป็นเรื่องดีสำหรับผู้ใช้