ข้อมูลเข้ากำลังส่งไปยัง Compositor
นี่เป็นบล็อกชุดที่ 4 จากทั้งหมด 4 บล็อกที่เจาะลึกการทำงานของ Chrome โดยเราจะตรวจสอบวิธีที่ Chrome จัดการโค้ดของเราเพื่อแสดงเว็บไซต์ ในโพสต์ก่อนหน้า เราได้ดูกระบวนการแสดงผลและเรียนรู้เกี่ยวกับคอมโพสิตอร์ ในโพสต์นี้ เราจะมาดูวิธีที่คอมโพสิตอร์ช่วยให้การโต้ตอบราบรื่นเมื่อผู้ใช้ป้อนข้อมูล
เหตุการณ์อินพุตจากมุมมองของเบราว์เซอร์
เมื่อได้ยินคำว่า "เหตุการณ์อินพุต" คุณอาจนึกถึงแค่การพิมพ์ในกล่องข้อความหรือการคลิกเมาส์ แต่จากมุมมองของเบราว์เซอร์ อินพุตหมายถึงท่าทางสัมผัสใดๆ จากผู้ใช้ การเลื่อนด้วยปุ่มลูกกลิ้งเมาส์เป็นเหตุการณ์การป้อนข้อมูล และการสัมผัสหรือการวางเมาส์เหนือเป็นเหตุการณ์การป้อนข้อมูลเช่นกัน
เมื่อผู้ใช้ทำท่าทางสัมผัสบนหน้าจอ กระบวนการของเบราว์เซอร์จะเป็นผู้รับท่าทางสัมผัสในตอนแรก อย่างไรก็ตาม กระบวนการของเบราว์เซอร์จะรับรู้เฉพาะตําแหน่งที่ท่าทางสัมผัสนั้นเกิดขึ้น เนื่องจากเนื้อหาภายในแท็บจะจัดการโดยกระบวนการแสดงผล ดังนั้น กระบวนการของเบราว์เซอร์จะส่งประเภทเหตุการณ์ (เช่น touchstart) และพิกัดของเหตุการณ์ไปยังกระบวนการของโปรแกรมแสดงผล กระบวนการแสดงผลจะจัดการเหตุการณ์อย่างเหมาะสมโดยค้นหาเป้าหมายเหตุการณ์และเรียกใช้ Listener เหตุการณ์ที่แนบอยู่
Compositor ได้รับเหตุการณ์การป้อนข้อมูล
ในโพสต์ก่อนหน้า เราได้ดูวิธีที่คอมโพสิตอร์จัดการการเลื่อนอย่างราบรื่นด้วยการคอมโพสิตเลเยอร์แบบแรสเตอร์ หากไม่ได้แนบโปรแกรมรับฟังเหตุการณ์อินพุตไว้กับหน้าเว็บ ด้ายคอมโพสิเตอร์จะสร้างเฟรมคอมโพสิต์ใหม่ได้โดยไม่ขึ้นอยู่กับด้ายหลักเลย แต่จะเกิดอะไรขึ้นหากมีการแนบโปรแกรมรับฟังเหตุการณ์บางอย่างไว้กับหน้าเว็บ ด้ายคอมโพสิตจะรู้ได้อย่างไรว่าต้องจัดการเหตุการณ์หรือไม่
ทำความเข้าใจภูมิภาคที่เลื่อนได้แบบช้า
เนื่องจากการทำงานของ JavaScript เป็นหน้าที่ของเธรดหลัก เมื่อมีการคอมโพสิทหน้าเว็บ เธรดคอมโพสิทจะทําเครื่องหมายส่วนของหน้าเว็บที่มีตัวแฮนเดิลเหตุการณ์แนบอยู่เป็น "ส่วนที่เลื่อนแบบเร็วไม่ได้" การมีข้อมูลนี้ช่วยให้เธรดคอมโพสิตอร์สามารถส่งเหตุการณ์อินพุตไปยังเธรดหลักได้หากเหตุการณ์เกิดขึ้นในภูมิภาคนั้น หากเหตุการณ์อินพุตมาจากภายนอกภูมิภาคนี้ ด้ายคอมโพสิตจะยังคงคอมโพสเฟรมใหม่ต่อไปโดยไม่ต้องรอด้ายหลัก
โปรดระมัดระวังเมื่อเขียนเครื่องจัดการเหตุการณ์
รูปแบบการจัดการเหตุการณ์ที่พบบ่อยในการพัฒนาเว็บคือการมอบหมายเหตุการณ์ เนื่องจากเหตุการณ์จะทวีขึ้น คุณจึงสามารถแนบตัวแฮนเดิลเหตุการณ์ 1 รายการที่องค์ประกอบบนสุดและมอบหมายงานตามเป้าหมายเหตุการณ์ได้ คุณอาจเคยเห็นหรือเขียนโค้ดอย่างเช่นด้านล่าง
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault();
}
});
เนื่องจากคุณเขียนเพียง 1 ตัวแฮนเดิลเหตุการณ์สําหรับองค์ประกอบทั้งหมด รูปแบบการมอบหมายเหตุการณ์นี้จึงน่าสนใจในแง่ของการยศาสตร์ อย่างไรก็ตาม หากคุณดูโค้ดนี้จากมุมมองของเบราว์เซอร์ ตอนนี้ทั้งหน้าเว็บจะมีสถานะเป็นภูมิภาคที่เลื่อนแบบเร็วไม่ได้ ซึ่งหมายความว่าแม้ว่าแอปพลิเคชันจะไม่สนใจอินพุตจากบางส่วนของหน้า แต่เธรดคอมโพสิตก็ต้องสื่อสารกับเธรดหลักและรอทุกครั้งที่มีเหตุการณ์อินพุตเข้ามา ความสามารถในการเลื่อนอย่างราบรื่นของคอมโพสิตเตอร์จึงถูกทำลาย
คุณสามารถส่งตัวเลือก passive: true ใน event listener เพื่อลดปัญหานี้ ซึ่งบอกเป็นนัยแก่เบราว์เซอร์ว่าคุณยังคงต้องการฟังเหตุการณ์ในเธรดหลัก แต่คอมโพสิตสามารถดำเนินการต่อและคอมโพสเฟรมใหม่ได้เช่นกัน
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault()
}
}, {passive: true});
ตรวจสอบว่ายกเลิกเหตุการณ์ได้หรือไม่
สมมติว่าคุณมีกล่องในหน้าเว็บที่ต้องการจำกัดทิศทางการเลื่อนให้เป็นการเลื่อนแนวนอนเท่านั้น
การใช้ตัวเลือก passive: true ในเหตุการณ์เคอร์เซอร์หมายความว่าการเลื่อนหน้าเว็บจะราบรื่น แต่การเลื่อนแนวตั้งอาจเริ่มต้นขึ้นเมื่อคุณต้องการ preventDefault เพื่อจำกัดทิศทางการเลื่อน คุณสามารถตรวจสอบข้อมูลนี้โดยใช้วิธีการ event.cancelable
document.body.addEventListener('pointermove', event => {
if (event.cancelable) {
event.preventDefault(); // block the native scroll
/*
* do what you want the application to do here
*/
}
}, {passive: true});
หรือจะใช้กฎ CSS เช่น touch-action เพื่อนำตัวแฮนเดิลเหตุการณ์ออกทั้งหมดก็ได้
#area {
touch-action: pan-x;
}
การค้นหาเป้าหมายของเหตุการณ์
เมื่อเธรดคอมโพสิตส่งเหตุการณ์อินพุตไปยังเธรดหลัก สิ่งแรกที่จะทำงานคือ Hit Test เพื่อค้นหาเป้าหมายเหตุการณ์ การทดสอบการทํางานใช้ข้อมูลบันทึกการวาดภาพที่สร้างขึ้นในกระบวนการแสดงผลเพื่อค้นหาสิ่งที่อยู่ใต้พิกัดจุดที่เหตุการณ์เกิดขึ้น
การลดการส่งเหตุการณ์ไปยังเธรดหลัก
ในโพสต์ก่อนหน้า เราได้พูดถึงวิธีที่จอแสดงผลทั่วไปรีเฟรชหน้าจอ 60 ครั้งต่อวินาที และวิธีที่เราต้องตามทันจังหวะเพื่อให้ภาพเคลื่อนไหวราบรื่น สําหรับอินพุต อุปกรณ์หน้าจอสัมผัสทั่วไปจะส่งเหตุการณ์การสัมผัส 60-120 ครั้งต่อวินาที และเมาส์ทั่วไปจะส่งเหตุการณ์ 100 ครั้งต่อวินาที เหตุการณ์อินพุตมีความแม่นยำสูงกว่าที่หน้าจอจะรีเฟรชได้
หากมีการส่งเหตุการณ์ต่อเนื่อง เช่น touchmove ไปยังเธรดหลัก 120 ครั้งต่อวินาที ก็อาจทริกเกอร์การทดสอบ Hit และการดำเนินการ JavaScript มากเกินไปเมื่อเทียบกับความช้าในการรีเฟรชหน้าจอ
Chrome จะรวมเหตุการณ์ต่อเนื่อง (เช่น wheel, mousewheel, mousemove, pointermove, touchmove) และเลื่อนการส่งออกจนกว่าจะถึงเวลาก่อน requestAnimationFrame ถัดไป เพื่อลดการเรียกใช้เธรดหลักที่มากเกินไป
ระบบจะส่งเหตุการณ์แบบไม่ต่อเนื่อง เช่น keydown, keyup, mouseup, mousedown, touchstart และ touchend ทันที
ใช้ getCoalescedEvents เพื่อรับเหตุการณ์ภายในเฟรม
สําหรับเว็บแอปพลิเคชันส่วนใหญ่ เหตุการณ์ที่รวมควรเพียงพอที่จะมอบประสบการณ์การใช้งานที่ดีแก่ผู้ใช้
อย่างไรก็ตาม หากคุณกำลังสร้างสิ่งต่างๆ เช่น แอปพลิเคชันการวาดและวางเส้นทางตามพิกัด touchmove คุณอาจสูญเสียพิกัดระหว่างกลางเพื่อวาดเส้นที่ราบรื่น ในกรณีนี้ คุณสามารถใช้เมธอด getCoalescedEvents ในเหตุการณ์เคอร์เซอร์เพื่อรับข้อมูลเกี่ยวกับเหตุการณ์ที่รวมกันเหล่านั้น
window.addEventListener('pointermove', event => {
const events = event.getCoalescedEvents();
for (let event of events) {
const x = event.pageX;
const y = event.pageY;
// draw a line using x and y coordinates.
}
});
ขั้นตอนถัดไป
ในซีรีส์นี้ เราได้พูดถึงวิธีการทำงานของเว็บเบราว์เซอร์ หากคุณไม่เคยสงสัยว่าเหตุใดเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์จึงแนะนำให้เพิ่ม {passive: true} ในตัวแฮนเดิลเหตุการณ์ หรือเหตุใดคุณจึงควรเขียนแอตทริบิวต์ async ในแท็กสคริปต์ เราหวังว่าชุดบทความนี้จะอธิบายเหตุผลที่เบราว์เซอร์ต้องใช้ข้อมูลเหล่านั้นเพื่อให้ประสบการณ์การใช้งานเว็บรวดเร็วและราบรื่นยิ่งขึ้น
ใช้ Lighthouse
หากต้องการให้โค้ดของคุณทำงานได้ดีกับเบราว์เซอร์แต่ไม่รู้จะเริ่มต้นอย่างไร Lighthouse เป็นเครื่องมือที่ดำเนินการตรวจสอบเว็บไซต์และให้รายงานเกี่ยวกับสิ่งที่ทําได้ดีและสิ่งที่ควรปรับปรุง การอ่านรายการการตรวจสอบยังช่วยให้คุณทราบถึงสิ่งที่เบราว์เซอร์ให้ความสำคัญด้วย
ดูวิธีวัดประสิทธิภาพ
การปรับแต่งประสิทธิภาพอาจแตกต่างกันไปสำหรับเว็บไซต์แต่ละแห่ง คุณจึงควรวัดประสิทธิภาพของเว็บไซต์และตัดสินใจว่าสิ่งใดเหมาะกับเว็บไซต์ของคุณมากที่สุด ทีมเครื่องมือสำหรับนักพัฒนาเว็บของ Chrome มีบทแนะนำเกี่ยวกับวิธีวัดประสิทธิภาพของเว็บไซต์
เพิ่มนโยบายฟีเจอร์ลงในเว็บไซต์
หากต้องการดำเนินการเพิ่มเติม นโยบายฟีเจอร์คือฟีเจอร์ใหม่ของแพลตฟอร์มเว็บที่จะช่วยควบคุมคุณเมื่อสร้างโปรเจ็กต์ การเปิดใช้นโยบายฟีเจอร์เป็นการรับประกันลักษณะการทํางานบางอย่างของแอปและป้องกันไม่ให้คุณทําผิดพลาด
เช่น หากต้องการให้แอปไม่บล็อกการแยกวิเคราะห์ คุณสามารถเรียกใช้แอปในนโยบายสคริปต์แบบซิงค์ เมื่อเปิดใช้ sync-script: 'none' ระบบจะป้องกันไม่ให้ JavaScript ที่บล็อกโปรแกรมวิเคราะห์ทำงาน วิธีนี้จะช่วยป้องกันไม่ให้โค้ดบล็อกโปรแกรมแยกวิเคราะห์ และเบราว์เซอร์ไม่ต้องกังวลเกี่ยวกับการหยุดโปรแกรมแยกวิเคราะห์ชั่วคราว
สรุป
เมื่อเริ่มสร้างเว็บไซต์ ฉันแทบจะสนใจแต่วิธีเขียนโค้ดและสิ่งที่จะช่วยให้ทำงานได้อย่างมีประสิทธิภาพมากขึ้น สิ่งเหล่านี้สำคัญ แต่เราก็ควรคำนึงถึงวิธีที่เบราว์เซอร์ใช้โค้ดที่เราเขียนด้วย เบราว์เซอร์สมัยใหม่ได้ลงทุนและยังคงลงทุนอย่างต่อเนื่องเพื่อมอบประสบการณ์การใช้งานเว็บที่ดีขึ้นให้แก่ผู้ใช้ การจัดระเบียบโค้ดของเราให้เหมาะกับเบราว์เซอร์จะช่วยปรับปรุงประสบการณ์ของผู้ใช้ เราหวังว่าคุณจะร่วมทำภารกิจนี้กับเราเพื่อดูแลเบราว์เซอร์
ขอขอบคุณอย่างยิ่งทุกคนที่ได้อ่านฉบับร่างต้นๆ ของชุดนี้ ซึ่งรวมถึง (แต่ไม่จำกัดเพียง) Alex Russell, Paul Irish, Meggin Kearney, Eric Bidelman, Mathias Bynens, Addy Osmani, Kinuko Yasuda, Nasko Oskov และ Charlie Reis
คุณชอบชุดนี้ไหม หากมีข้อสงสัยหรือคำแนะนำสำหรับโพสต์ในอนาคต เรายินดีรับฟังจากคุณในส่วนความคิดเห็นด้านล่างหรือที่ @kosamari บน Twitter