API วงจรการใช้งานหน้าเว็บ

การรองรับเบราว์เซอร์

  • Chrome: 68
  • ขอบ: 79
  • Firefox: ไม่สนับสนุน
  • Safari: ไม่รองรับ

บางครั้งเบราว์เซอร์สมัยใหม่จะระงับหน้าเว็บหรือทิ้งหน้าเว็บไปทั้งหมด ทรัพยากรระบบมีจำกัด ในอนาคต เบราว์เซอร์ต้องการทำสิ่งนี้ เพื่อให้ใช้พลังงานและหน่วยความจำน้อยลง Page Lifecycle API มีฮุกวงจรเพื่อให้หน้าเว็บของคุณสามารถจัดการเบราว์เซอร์เหล่านี้ได้อย่างปลอดภัย แทรกแซงได้โดยไม่ส่งผลกระทบต่อประสบการณ์ของผู้ใช้ ดูที่ API เพื่อ ดูว่าคุณควรนำฟีเจอร์เหล่านี้ไปใช้กับแอปพลิเคชันของคุณไหม

ข้อมูลเบื้องต้น

วงจรแอปพลิเคชันคือวิธีหลักที่ระบบปฏิบัติการสมัยใหม่จัดการ ที่ไม่ซับซ้อน ใน Android, iOS และ Windows เวอร์ชันล่าสุด คุณสามารถเริ่มใช้แอป ถูกหยุดการทำงานได้ตลอดเวลาโดยระบบปฏิบัติการ ทำให้แพลตฟอร์มเหล่านี้สามารถเพิ่มประสิทธิภาพ และ จัดสรรทรัพยากรที่มีประโยชน์ต่อผู้ใช้มากที่สุด

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

แม้ว่าแพลตฟอร์มเว็บจะมีเหตุการณ์ที่เกี่ยวข้องกับสถานะของวงจรมาเป็นเวลานานแล้ว — เช่น load unload และ visibilitychange — กิจกรรมเหล่านี้อนุญาตให้เฉพาะนักพัฒนาซอฟต์แวร์เท่านั้น เพื่อตอบสนองต่อการเปลี่ยนแปลงสถานะของวงจรที่เริ่มต้นโดยผู้ใช้ เพื่อให้ใช้งานเว็บได้ เชื่อถือได้บนอุปกรณ์ที่ใช้พลังงานต่ำ (และคำนึงถึงทรัพยากรโดยทั่วไปเกี่ยวกับ ทุกแพลตฟอร์ม) ที่เบราว์เซอร์ต้องการวิธีเชิงรุกในการเรียกคืนและจัดสรรระบบใหม่ ที่ไม่ซับซ้อน

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

ปัญหาก็คือนักพัฒนาซอฟต์แวร์ไม่มีทางที่จะเตรียมตัวรับมือ การฝึกฝนที่เริ่มต้นโดยระบบ หรือแม้กระทั่งรู้ว่ากำลังเกิดขึ้น ซึ่งหมายความว่า เบราว์เซอร์จึงต้องระมัดระวังเสมอ หรือมีโอกาสที่จะทำให้หน้าเว็บเสียหาย

Page Lifecycle API พยายามแก้ไขปัญหานี้โดย:

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

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

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

ภาพรวมสถานะและเหตุการณ์ในวงจรของหน้า

สถานะวงจรทั้งหมดของหน้าเว็บจะแยกกันและแยกจากกัน ซึ่งหมายความว่าหน้าเว็บ สามารถมีสถานะได้ทีละหนึ่งสถานะเท่านั้น และการเปลี่ยนแปลงส่วนใหญ่ต่อสถานะวงจรของหน้าเว็บ โดยทั่วไปจะสามารถสังเกตการณ์ได้ผ่านเหตุการณ์ DOM (ดูข้อยกเว้นในคำแนะนำของนักพัฒนาซอฟต์แวร์สำหรับแต่ละสถานะ)

บางทีวิธีที่ง่ายที่สุดในการอธิบายสถานะของวงจรหน้าเว็บ รวมถึง เหตุการณ์ที่ส่งสัญญาณถึงการเปลี่ยนแปลงระหว่างเหตุการณ์ทั้งสอง ได้แก่ แผนภาพ

ภาพสถานะและโฟลว์เหตุการณ์ตามที่อธิบายไว้ในเอกสารนี้
สถานะและโฟลว์เหตุการณ์ของ API อายุการใช้งานของหน้าเว็บ

รัฐ

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

รัฐ คำอธิบาย
ใช้งานอยู่

หน้าเว็บจะอยู่ในสถานะใช้งานอยู่หากมองเห็นได้และ โฟกัสอินพุต

สถานะก่อนหน้าที่เป็นไปได้มีดังนี้
พาสซีฟ (ผ่านกิจกรรม focus)
ค้าง (ผ่านเหตุการณ์ resume จากนั้น pageshow กิจกรรม)

สถานะถัดไปที่เป็นไปได้มีดังนี้
พาสซีฟ (ผ่านกิจกรรม blur)

เชิงรับ

หน้าเว็บจะอยู่ในสถานะพาสซีฟหากมองเห็นได้และ ไม่ได้โฟกัสอินพุต

สถานะก่อนหน้าที่เป็นไปได้มีดังนี้
ใช้งาน (ผ่านกิจกรรม blur)
ซ่อนอยู่ (ผ่านทาง visibilitychange เหตุการณ์)
ค้าง (ผ่านเหตุการณ์ resume จากนั้น pageshow กิจกรรม)

สถานะถัดไปที่เป็นไปได้มีดังนี้
ใช้งาน (ผ่านกิจกรรม focus)
ซ่อนอยู่ (ผ่านทาง visibilitychange)

ซ่อนอยู่

หน้าเว็บจะอยู่ในสถานะซ่อน หากไม่เห็น (และไม่เห็น ถูกตรึง ทิ้ง หรือสิ้นสุดการใช้งาน)

สถานะก่อนหน้าที่เป็นไปได้มีดังนี้
พาสซีฟ (ผ่านทาง visibilitychange)
ค้าง (ผ่านเหตุการณ์ resume จากนั้น pageshow กิจกรรม)

สถานะถัดไปที่เป็นไปได้มีดังนี้
พาสซีฟ (ผ่านทาง visibilitychange)
ค้าง (ผ่านกิจกรรม freeze)
ยกเลิกแล้ว (ไม่มีเหตุการณ์เริ่มทำงาน)
สิ้นสุด (ไม่มีเหตุการณ์เริ่มทำงาน)

หยุดการทำงาน

ในสถานะถูกตรึง เบราว์เซอร์จะระงับการดำเนินการ เพิ่มพื้นที่ว่างได้ งานในส่วนของหน้า ไว้ในคิวงานจนกว่าหน้าจะเลิกตรึง ซึ่งหมายความว่า ตัวจับเวลา JavaScript และ Callback ที่ดึงข้อมูลไม่ทำงาน ทำงานอยู่แล้ว งานต่างๆ ให้เสร็จได้ (ที่สำคัญที่สุดคือ freeze Callback) แต่อาจถูกจำกัดในส่วน สามารถทำได้และระยะเวลาที่แสดง

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

สถานะก่อนหน้าที่เป็นไปได้มีดังนี้
ซ่อนอยู่ (ผ่านกิจกรรม freeze)

สถานะถัดไปที่เป็นไปได้มีดังนี้
ใช้งาน (ผ่านเหตุการณ์ resume จากนั้น pageshow กิจกรรม)
พาสซีฟ (ผ่านเหตุการณ์ resume จากนั้น pageshow กิจกรรม)
ซ่อนอยู่ (ผ่านกิจกรรม resume)
ยกเลิกแล้ว (ไม่มีเหตุการณ์เริ่มทำงาน)

สิ้นสุดการใช้งาน

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

สถานะก่อนหน้าที่เป็นไปได้มีดังนี้
ซ่อนอยู่ (ผ่านกิจกรรม pagehide)

สถานะถัดไปที่เป็นไปได้มีดังนี้
ไม่มี

ทิ้งแล้ว

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

ในสถานะยกเลิกแล้ว แท็บ (รวมถึงชื่อแท็บและไอคอน Fav) มักแสดงต่อผู้ใช้ แม้ว่าหน้าเว็บนั้นจะหายไปแล้ว

สถานะก่อนหน้าที่เป็นไปได้มีดังนี้
ซ่อนอยู่ (ไม่มีเหตุการณ์เริ่มทำงาน)
ตรึง (ไม่มีเหตุการณ์เริ่มทำงาน)

สถานะถัดไปที่เป็นไปได้มีดังนี้
ไม่มี

กิจกรรม

เบราว์เซอร์จะส่งเหตุการณ์จำนวนมาก แต่มีเพียงไม่กี่เหตุการณ์เท่านั้นที่ส่งสัญญาณ การเปลี่ยนแปลงในสถานะวงจรของหน้า ตารางต่อไปนี้จะสรุปเหตุการณ์ทั้งหมด ที่เกี่ยวข้องกับวงจรและระบุสถานะของสถานะที่อาจเปลี่ยนและมาจาก

ชื่อ รายละเอียด
focus

องค์ประกอบ DOM ได้รับการโฟกัส

หมายเหตุ: เหตุการณ์ focus ไม่ เป็นสัญญาณการเปลี่ยนสถานะ สัญญาณนี้จะส่งสัญญาณการเปลี่ยนแปลงสถานะก็ต่อเมื่อ หน้านี้ไม่เคยมีโฟกัสการป้อนข้อมูลมาก่อน

สถานะก่อนหน้าที่เป็นไปได้มีดังนี้
พาสซีฟ

สถานะปัจจุบันที่เป็นไปได้มีดังนี้
ใช้งาน

blur

องค์ประกอบ DOM สูญเสียโฟกัส

หมายเหตุ: เหตุการณ์ blur ไม่ เป็นสัญญาณการเปลี่ยนสถานะ สัญญาณนี้จะส่งสัญญาณการเปลี่ยนแปลงสถานะก็ต่อเมื่อ หน้าเว็บไม่มีโฟกัสอินพุตแล้ว (เช่น หน้าเว็บไม่ได้เปลี่ยน จากองค์ประกอบหนึ่งไปยังอีกองค์ประกอบหนึ่ง)

สถานะก่อนหน้าที่เป็นไปได้มีดังนี้
ใช้งาน

สถานะปัจจุบันที่เป็นไปได้มีดังนี้
พาสซีฟ

visibilitychange

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

สถานะก่อนหน้าที่เป็นไปได้มีดังนี้
แพสซีฟ
ซ่อนอยู่

สถานะปัจจุบันที่เป็นไปได้มีดังนี้
แพสซีฟ
ซ่อนอยู่

วันที่ freeze *

หน้านี้เพิ่งจะถูกตรึง ช่วง งานที่ตรึงได้ในคิวงานของหน้าจะไม่เริ่ม

สถานะก่อนหน้าที่เป็นไปได้มีดังนี้
ซ่อนอยู่

สถานะปัจจุบันที่เป็นไปได้มีดังนี้
ค้าง

วันที่ resume *

เบราว์เซอร์ได้เปิดหน้าเว็บที่ค้างอีกครั้ง

สถานะก่อนหน้าที่เป็นไปได้มีดังนี้
ค้าง

สถานะปัจจุบันที่เป็นไปได้มีดังนี้
ใช้งาน (หากตามด้วย pageshow)
พาสซีฟ (หากตามด้วย pageshow)
ซ่อนอยู่

pageshow

กำลังข้ามรายการประวัติเซสชัน

ซึ่งอาจเป็นการโหลดหน้าเว็บใหม่หรือหน้าเว็บที่นำมาจาก back/Forward Cache หากหน้า มาจากแคชย้อนหลัง พร็อพเพอร์ตี้ persisted คือ true หากไม่ใช่ false

สถานะก่อนหน้าที่เป็นไปได้มีดังนี้
ค้าง (ก resume ก็จะเริ่มทำงานเช่นกัน)

สถานะปัจจุบันที่เป็นไปได้มีดังนี้
ใช้งาน
เชิงรับ
ซ่อน

pagehide

กำลังข้ามผ่านรายการประวัติเซสชัน

หากผู้ใช้ไปยังหน้าอื่นและเบราว์เซอร์สามารถเพิ่ม หน้าปัจจุบันเป็น back/Forward แคช เพื่อนำมาใช้ใหม่ในภายหลัง พร็อพเพอร์ตี้ persisted ของเหตุการณ์ มีค่า true เมื่อ true หน้านี้กำลังเข้าสู่ สถานะถูกหยุดลง มิเช่นนั้น จะเข้าสู่สถานะยุติ

สถานะก่อนหน้าที่เป็นไปได้มีดังนี้
ซ่อนอยู่

สถานะปัจจุบันที่เป็นไปได้มีดังนี้
ค้าง (event.persisted ถูกต้อง freezeกิจกรรมที่ตามมา)
สิ้นสุด (event.persisted เป็นเท็จ unload กิจกรรมที่ตามมา)

beforeunload

ระบบกำลังยกเลิกการโหลดหน้าต่าง เอกสาร และทรัพยากร เอกสารยังมองเห็นได้อยู่และคุณยังยกเลิกกิจกรรมได้ที่ คะแนน

สำคัญ: เหตุการณ์ beforeunload ควรใช้เพื่อแจ้งเตือนผู้ใช้เกี่ยวกับการเปลี่ยนแปลงที่ไม่ได้บันทึกเท่านั้น เมื่อสิ่งเหล่านั้น การเปลี่ยนแปลงที่บันทึกไว้ ควรนำกิจกรรมดังกล่าวออก คุณไม่ควรเป็น ลงในหน้าเว็บอย่างไม่มีเงื่อนไข เนื่องจากอาจส่งผลเสียต่อประสิทธิภาพ บางกรณี โปรดดูอินเทอร์เฟซเดิม ส่วน API เพื่อดูรายละเอียด

สถานะก่อนหน้าที่เป็นไปได้มีดังนี้
ซ่อนอยู่

สถานะปัจจุบันที่เป็นไปได้มีดังนี้
สิ้นสุด

unload

กำลังยกเลิกการโหลดหน้านี้

คำเตือน: เราไม่แนะนำให้ใช้เหตุการณ์ unload เนื่องจาก ไม่น่าเชื่อถือและอาจส่งผลต่อประสิทธิภาพในบางกรณี โปรดดู ส่วน API เดิม เพื่อดูรายละเอียดเพิ่มเติม

สถานะก่อนหน้าที่เป็นไปได้มีดังนี้
ซ่อนอยู่

สถานะปัจจุบันที่เป็นไปได้มีดังนี้
สิ้นสุด

* ระบุเหตุการณ์ใหม่ที่กำหนดโดย 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()
  • กำหนดฟังก์ชันที่ยอมรับสถานะถัดไป และหากมีการเปลี่ยนแปลง จะบันทึกสถานะการเปลี่ยนแปลงไปยังคอนโซล
  • การเพิ่ม การจับภาพ Listener เหตุการณ์สำหรับเหตุการณ์ในวงจรที่จำเป็นทั้งหมด ซึ่งสุดท้ายแล้วก็จะเป็นการเรียก logStateChange() ผ่านในสถานะถัดไป

สิ่งหนึ่งที่ควรทราบเกี่ยวกับโค้ดคือจะมีการเพิ่ม Listener เหตุการณ์ทั้งหมด ถึง window ทุกอย่างก็ให้ที่ {capture: true} ซึ่งอาจเป็นเพราะสาเหตุต่อไปนี้

  • เหตุการณ์ในวงจรของหน้าบางเหตุการณ์ไม่ได้มีเป้าหมายเหมือนกัน pagehide และ pageshow เริ่มทำงานใน window visibilitychange, freeze และ resume เริ่มทำงานใน document และ focus และ blur เริ่มทำงานใน เอลิเมนต์ DOM ที่เกี่ยวข้อง
  • กิจกรรมเหล่านี้ส่วนใหญ่ไม่แสดงเป็นบับเบิล ซึ่งหมายความว่าเพิ่มไม่ได้ Listener เหตุการณ์ที่ไม่ใช่การบันทึกไปยังองค์ประกอบระดับบนที่พบบ่อยและสังเกตทั้งหมด ทั้งหมด
  • ระยะการจับภาพจะทำงานก่อนระยะเป้าหมายหรือช่วงลูกโป่ง ดังนั้นการเพิ่ม Listener ในนั้นช่วยให้มั่นใจว่าตนจะทำงานก่อนที่โค้ดอื่นๆ จะยกเลิกได้

คำแนะนำจากนักพัฒนาแอปสำหรับแต่ละรัฐ

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

ตัวอย่างเช่น การแสดงการแจ้งเตือนชั่วคราวอาจไม่เหมาะสมอย่างชัดเจน ให้กับผู้ใช้หากหน้าเว็บมีสถานะซ่อน ขณะที่ตัวอย่างนี้ ยังมีคำแนะนำอื่นๆ ที่ไม่ชัดเจนซึ่งมีคุณค่า แจกแจง

รัฐ คำแนะนำสำหรับนักพัฒนาซอฟต์แวร์
Active

สถานะใช้งานอยู่เป็นเวลาที่สำคัญที่สุดสำหรับผู้ใช้และด้วยเหตุนี้ ช่วงเวลาที่สำคัญที่สุดที่หน้าเว็บของคุณจะ ตอบสนองต่อข้อมูลจากผู้ใช้

งานที่ไม่ใช้ UI ที่อาจบล็อกเทรดหลักควรลดลำดับความสำคัญลง ถึง ระยะเวลาที่ไม่มีการใช้งานหรือ ให้กับ Web Worker

Passive

ในสถานะแพสซีฟ ผู้ใช้ไม่ได้โต้ตอบกับหน้าเว็บ แต่จะยังเห็นได้อยู่ ซึ่งหมายความว่าการอัปเดต UI และภาพเคลื่อนไหว อย่างราบรื่น แต่ช่วงเวลาที่มีการอัปเดตเหล่านี้ไม่สำคัญนัก

เมื่อหน้าเว็บเปลี่ยนจากแอ็กทีฟเป็นแพสซีฟ ถึงเวลายืนยันสถานะแอปพลิเคชันที่ไม่ได้บันทึกแล้ว

Hidden

เมื่อหน้าเว็บเปลี่ยนจากแฝงเป็นซ่อน เป็นไปได้ว่าผู้ใช้จะไม่โต้ตอบกับแอปอีกจนกว่าจะโหลดใหม่

การเปลี่ยนเป็น "ซ่อน" มักเป็นการเปลี่ยนแปลงสถานะสุดท้ายด้วย ซึ่งนักพัฒนาซอฟต์แวร์จะสังเกตได้อย่างน่าเชื่อถือ (โดยเฉพาะอย่างยิ่งใน บนอุปกรณ์เคลื่อนที่ เนื่องจากผู้ใช้สามารถปิดแท็บหรือแอปเบราว์เซอร์ได้ และ beforeunload pagehide และ unload เหตุการณ์จะไม่เริ่มทำงานในกรณีเหล่านั้น)

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

นอกจากนี้ คุณควรหยุดทำการอัปเดต UI ด้วย (เนื่องจากจะไม่มีใครเห็นการอัปเดตเหล่านั้น ตามที่ผู้ใช้ไม่ได้) และคุณควรหยุดงานใดๆ ที่ผู้ใช้ไม่ต้องการ ทำงานในพื้นหลัง

Frozen

ในสถานะค้าง งานที่ตรึงได้ใน คิวงานจะถูกระงับจนกว่าหน้าเว็บจะเลิกตรึง ซึ่งอาจ ไม่เคยเกิดขึ้นเลย (เช่น หากหน้าเว็บถูกยกเลิก)

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

โดยเฉพาะอย่างยิ่ง คุณต้อง

นอกจากนี้ คุณยังควรคงสถานะมุมมองแบบไดนามิกเอาไว้ด้วย (เช่น ตำแหน่งเลื่อน ในมุมมองรายการแบบไม่สิ้นสุด) เพื่อ sessionStorage (หรือ IndexedDB ผ่าน commit()) ที่คุณต้องการกู้คืนหากหน้าเว็บถูก ทิ้งและโหลดซ้ำในภายหลัง

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

Terminated

โดยทั่วไปแล้วคุณไม่ต้องดำเนินการใดๆ เมื่อเปลี่ยนหน้า เป็นสถานะสิ้นสุด

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

โปรดทราบด้วยว่า (ดังที่ได้กล่าวไว้ในคำแนะนำสำหรับ สถานะซ่อนอยู่) นักพัฒนาซอฟต์แวร์จะต้องตระหนักถึง ว่าการเปลี่ยนเป็นสภาวะที่ ถูกยุติ นั้นเชื่อถือได้ ในหลายกรณี (โดยเฉพาะบนอุปกรณ์เคลื่อนที่) ดังนั้นนักพัฒนาซอฟต์แวร์ที่พึ่งพา เกี่ยวกับเหตุการณ์การสิ้นสุด (เช่น beforeunload pagehide และ unload) อาจสูญเสียข้อมูล

Discarded

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

ดังนั้น คุณจึงควรเตรียมพร้อมสำหรับความเป็นไปได้ที่จะยกเลิก เปลี่ยนจากซ่อนเป็นตรึงไว้ คุณก็จะสามารถ ตอบสนองต่อการกู้คืนหน้าเว็บที่ถูกยกเลิกเมื่อโหลดหน้าเว็บโดย กำลังตรวจสอบ document.wasDiscarded

เนื่องจากความน่าเชื่อถือและการเรียงลำดับเหตุการณ์ในวงจรไม่ใช่ มีการใช้งานอย่างสอดคล้องกันในทุกเบราว์เซอร์ วิธีที่ง่ายที่สุดในการทำตามคำแนะนำ ในตารางคือให้ใช้ 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 อาจทำให้หน้าเว็บไม่สามารถ มีสิทธิ์ใช้แคชย้อนหลัง เบราว์เซอร์สมัยใหม่ ไม่มีข้อจำกัดนี้ เพื่อเป็นการป้องกันไว้ก่อน บางเบราว์เซอร์จะไม่เริ่มทำงาน เหตุการณ์ beforeunload เมื่อพยายามใส่หน้าลงใน Back-Forward แคช ซึ่งหมายความว่าเหตุการณ์ไม่น่าเชื่อถือเป็นสัญญาณเมื่อสิ้นสุดเซสชัน นอกจากนี้บางเบราว์เซอร์ (รวมถึง Chrome) ต้องมีการโต้ตอบของผู้ใช้ในหน้าเว็บก่อนจึงจะอนุญาตเหตุการณ์ beforeunload ให้เริ่มการทำงานได้มากขึ้น ส่งผลต่อความเสถียรมากกว่าเดิม

ความแตกต่างอย่างหนึ่งระหว่าง beforeunload และ unload คือ การใช้งานที่ถูกต้องตามกฎหมายของ beforeunload เช่น เมื่อต้องการเตือนผู้ใช้ การเปลี่ยนแปลงที่ยังไม่ได้บันทึกจะสูญหายหากยกเลิกการโหลดหน้าต่อไป

เนื่องจากมีเหตุผลอันสมควรในการใช้ beforeunload เราจึงขอแนะนำให้คุณ เฉพาะเพิ่ม Listener beforeunload เมื่อผู้ใช้มีการเปลี่ยนแปลงที่ไม่ได้บันทึก จากนั้น ให้ลบทันทีเมื่อบันทึก

พูดอีกอย่างคือ อย่าทำเช่นนี้ (เพราะระบบจะเพิ่มผู้ฟัง beforeunload คน โดยไม่มีเงื่อนไข)

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);
  }
});

ให้ใช้วิธีนี้แทน (เนื่องจากจะเพิ่ม Listener beforeunload เฉพาะเมื่อ จำเป็น และนำออกเมื่อไม่จำเป็น):

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
  • การอัปเดตชื่อตารางหรือไอคอน Fav
  • การแสดงการแจ้งเตือน
  • กำลังส่งข้อความ Push

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

Back-Forward Cache คืออะไร

แคชย้อนหลังเป็นคำที่ใช้อธิบาย การเพิ่มประสิทธิภาพการนำทาง บางเบราว์เซอร์มีการใช้งานที่จะพิจารณาการใช้ ปุ่มไปข้างหน้าให้เร็วขึ้น

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

สำหรับ Intent และวัตถุประสงค์ทั้งหมด การตรึงนี้จะทำงานเหมือนกับ เบราว์เซอร์ที่ค้างจะทำหน้าที่ประหยัด CPU/แบตเตอรี่ ด้วยเหตุผลดังกล่าว ถือว่าเป็นส่วนหนึ่งของสถานะของวงจรค้าง

หากเรียกใช้ API แบบไม่พร้อมกันในสถานะที่ค้างหรือสิ้นสุดไม่ได้ ฉันจะบันทึกข้อมูลลงใน IndexedDB ได้อย่างไร

ในสถานะถูกระงับและสิ้นสุดแล้ว งานที่ตรึงได้ ในคิวงานของหน้าเว็บ ถูกระงับ ซึ่งหมายถึง API แบบไม่พร้อมกันและ API ที่อิงตาม Callback เช่น IndexedDB อาจไม่น่าเชื่อถือ

ในอนาคต เราจะเพิ่มเมธอด commit() ลงในออบเจ็กต์ IDBTransaction ซึ่งจะ ให้นักพัฒนาซอฟต์แวร์สามารถดำเนินการในธุรกรรมแบบเขียนเท่านั้นได้อย่างมีประสิทธิภาพ ที่ไม่จำเป็นต้องมี Callback กล่าวคือ หากนักพัฒนาซอฟต์แวร์แค่เขียน ไปยัง IndexedDB และไม่ดำเนินการธุรกรรมที่ซับซ้อนซึ่งประกอบด้วยการอ่าน และการเขียน เมธอด commit() จะเสร็จสิ้นได้ก่อนคิวงาน ถูกระงับ (สมมติว่าฐานข้อมูล IndexedDB เปิดอยู่แล้ว)

อย่างไรก็ตาม สำหรับโค้ดที่ต้องใช้ในปัจจุบัน นักพัฒนาซอฟต์แวร์มี 2 ตัวเลือก ได้แก่

  • ใช้พื้นที่เก็บข้อมูลเซสชัน: พื้นที่เก็บข้อมูลเซสชัน เป็นแบบซิงโครนัสและคงอยู่ในการยกเลิกหน้าเว็บ
  • ใช้ IndexedDB จาก Service Worker: โปรแกรมทำงานของบริการจะจัดเก็บข้อมูลได้ IndexedDB หลังจากที่หน้าเว็บถูกสิ้นสุดหรือยกเลิก ใน freeze หรือ Listener เหตุการณ์ pagehide ที่คุณสามารถส่งข้อมูลไปยัง Service Worker ผ่าน postMessage() และ Service Worker ช่วยจัดการบันทึกข้อมูลได้

การทดสอบแอปในสถานะหยุดค้างและถูกยกเลิก

หากต้องการทดสอบลักษณะการทำงานของแอปในสถานะหยุดค้างและถูกยกเลิก ให้ไปที่ chrome://discards เพื่อตรึงหรือทิ้ง แท็บที่เปิดอยู่

Chrome ยกเลิก UI
Chrome ยกเลิก UI

การดำเนินการนี้จะช่วยให้คุณมั่นใจได้ว่าหน้าเว็บจะจัดการ freeze และ resume อย่างถูกต้อง เหตุการณ์ต่างๆ รวมทั้งธง document.wasDiscarded เมื่อโหลดหน้าเว็บซ้ำหลังจาก ทิ้ง

สรุป

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

ยิ่งนักพัฒนาซอฟต์แวร์เริ่มใช้ Page Lifecycle API ใหม่มากเท่าใด ก็จะยิ่งปลอดภัยมากขึ้นเท่านั้น จะทำให้เบราว์เซอร์ตรึงและทิ้งหน้าที่ไม่ได้ใช้งาน ช่วงเวลานี้ หมายความว่าเบราว์เซอร์จะใช้หน่วยความจำ, CPU, แบตเตอรี่ และทรัพยากรเครือข่ายน้อยลง ซึ่งจะเป็นประโยชน์สำหรับผู้ใช้