ข้อมูลเข้ากำลังส่งไปยัง Compositor
นี่เป็นบล็อกชุดที่ 4 จากทั้งหมด 4 บล็อกที่เจาะลึกการทำงานของ Chrome โดยเราจะตรวจสอบวิธีที่ Chrome จัดการโค้ดของเราเพื่อแสดงเว็บไซต์ ในโพสต์ก่อนหน้า เราได้ดูกระบวนการแสดงผลและเรียนรู้เกี่ยวกับคอมโพสิตอร์ ในโพสต์นี้ เราจะมาดูวิธีที่คอมโพสิตอร์ช่วยให้การโต้ตอบราบรื่นเมื่อผู้ใช้ป้อนข้อมูล
เหตุการณ์อินพุตจากมุมมองของเบราว์เซอร์
เมื่อได้ยินคำว่า "เหตุการณ์อินพุต" คุณอาจนึกถึงแค่การพิมพ์ในกล่องข้อความหรือการคลิกเมาส์ แต่จากมุมมองของเบราว์เซอร์ อินพุตหมายถึงท่าทางสัมผัสใดๆ จากผู้ใช้ การเลื่อนด้วยปุ่มลูกกลิ้งเมาส์เป็นเหตุการณ์การป้อนข้อมูล และการสัมผัสหรือการวางเมาส์เหนือเป็นเหตุการณ์การป้อนข้อมูลเช่นกัน
เมื่อผู้ใช้ทำท่าทางสัมผัสบนหน้าจอ กระบวนการของเบราว์เซอร์จะเป็นผู้รับท่าทางสัมผัสในตอนแรก อย่างไรก็ตาม กระบวนการของเบราว์เซอร์จะรับรู้เฉพาะตําแหน่งที่ท่าทางสัมผัสนั้นเกิดขึ้น เนื่องจากเนื้อหาภายในแท็บจะจัดการโดยกระบวนการแสดงผล ดังนั้น กระบวนการของเบราว์เซอร์จะส่งประเภทเหตุการณ์ (เช่น touchstart
) และพิกัดของเหตุการณ์ไปยังกระบวนการของโปรแกรมแสดงผล กระบวนการแสดงผลจะจัดการเหตุการณ์อย่างเหมาะสมโดยค้นหาเป้าหมายเหตุการณ์และเรียกใช้ Listener เหตุการณ์ที่แนบอยู่
Compositor ได้รับเหตุการณ์การป้อนข้อมูล
ในโพสต์ก่อนหน้า เราได้ดูวิธีที่คอมโพสิตอร์จัดการการเลื่อนอย่างราบรื่นด้วยการคอมโพสิตเลเยอร์แบบแรสเตอร์ หากไม่ได้แนบโปรแกรมรับฟังเหตุการณ์อินพุตไว้กับหน้าเว็บ ด้ายคอมโพสิตจะสร้างเฟรมคอมโพสิตใหม่ได้โดยไม่ขึ้นอยู่กับด้ายหลักเลย แต่จะเกิดอะไรขึ้นหากมีการแนบโปรแกรมรับฟังเหตุการณ์บางอย่างไว้กับหน้าเว็บ ด้ายคอมโพสิตจะรู้ได้อย่างไรว่าต้องจัดการเหตุการณ์หรือไม่
ทำความเข้าใจภูมิภาคที่เลื่อนได้แบบช้า
เนื่องจากการทำงานของ JavaScript เป็นหน้าที่ของเธรดหลัก เมื่อมีการคอมโพสิทหน้าเว็บ เธรดคอมโพสิทจะทําเครื่องหมายส่วนของหน้าเว็บที่มีตัวแฮนเดิลเหตุการณ์แนบอยู่เป็น "ส่วนที่เลื่อนแบบเร็วไม่ได้" การมีข้อมูลนี้ช่วยให้เธรดคอมโพสิตตรวจสอบได้ว่าได้ส่งเหตุการณ์อินพุตไปยังเธรดหลักแล้ว หากเหตุการณ์เกิดขึ้นในภูมิภาคนั้น หากเหตุการณ์อินพุตมาจากภายนอกภูมิภาคนี้ ด้ายคอมโพสิตจะยังคงคอมโพสเฟรมใหม่ต่อไปโดยไม่ต้องรอด้ายหลัก
โปรดระมัดระวังเมื่อเขียนเครื่องจัดการเหตุการณ์
รูปแบบการจัดการเหตุการณ์ที่พบบ่อยในการพัฒนาเว็บคือการมอบหมายเหตุการณ์ เนื่องจากเหตุการณ์เป็นลูกโป่ง คุณจึงสามารถแนบตัวแฮนเดิลเหตุการณ์ 1 รายการที่องค์ประกอบบนสุดและมอบหมายงานตามเป้าหมายเหตุการณ์ได้ คุณอาจเคยเห็นหรือเขียนโค้ดอย่างเช่นด้านล่าง
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault();
}
});
เนื่องจากคุณจําเป็นต้องเขียนตัวจัดการเหตุการณ์เพียงรายการเดียวสําหรับองค์ประกอบทั้งหมด รูปแบบการมอบหมายเหตุการณ์นี้จึงน่าสนใจในแง่ของการยศาสตร์ อย่างไรก็ตาม หากคุณดูโค้ดนี้จากมุมมองของเบราว์เซอร์ ตอนนี้ทั้งหน้าเว็บจะมีสถานะเป็นภูมิภาคที่เลื่อนแบบเร็วไม่ได้ ซึ่งหมายความว่าแม้ว่าแอปพลิเคชันจะไม่สนใจอินพุตจากบางส่วนของหน้า แต่เธรดคอมโพสิตก็ต้องสื่อสารกับเธรดหลักและรอทุกครั้งที่มีเหตุการณ์อินพุตเข้ามา ความสามารถในการเลื่อนอย่างราบรื่นของคอมโพสิตเตอร์จึงถูกทำลาย
คุณสามารถส่งตัวเลือก 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