การทำงานภายในของกระบวนการแสดงภาพ
นี่คือส่วนที่ 3 ของซีรีส์บล็อก 4 ส่วนที่กล่าวถึงวิธีการทำงานของเบราว์เซอร์ ก่อนหน้านี้เราพูดถึงสถาปัตยกรรมการประมวลผลแบบหลายด้านและขั้นตอนการนำทาง ในโพสต์นี้ เราจะมาดูว่า เกิดอะไรขึ้นในกระบวนการของโหมดแสดงภาพ
กระบวนการแสดงผลมีแง่มุมต่างๆ ของประสิทธิภาพการทำงานของเว็บ เนื่องจากมีเหตุการณ์หลายอย่างเกิดขึ้นในกระบวนการแสดงผล โพสต์นี้จึงเป็นภาพรวมทั่วไปเท่านั้น หากคุณต้องการเจาะลึกมากขึ้น ส่วนประสิทธิภาพของหลักพื้นฐานในการทำเว็บไซต์มีแหล่งข้อมูลอีกมากมาย
กระบวนการของตัวแสดงผลจะจัดการเนื้อหาเว็บ
กระบวนการแสดงผลมีหน้าที่รับผิดชอบทุกอย่างที่เกิดขึ้นภายในแท็บ ในกระบวนการของโหมดแสดงภาพ เทรดหลักจะจัดการโค้ดส่วนใหญ่ที่คุณส่งให้ผู้ใช้ บางครั้งเทรดของผู้ปฏิบัติงานจะจัดการกับส่วนต่างๆ ของ JavaScript หากคุณใช้ Web Worker หรือ Service Worker นอกจากนี้ เทรดผสมและแรสเตอร์ยังทำงานภายในกระบวนการแสดงผลเพื่อให้แสดงผลหน้าได้อย่างมีประสิทธิภาพและราบรื่น
งานหลักของกระบวนการแสดงผลคือการเปลี่ยน HTML, CSS และ JavaScript เป็นหน้าเว็บที่ผู้ใช้โต้ตอบด้วยได้
กำลังแยกวิเคราะห์
การสร้าง DOM
เมื่อกระบวนการแสดงผลได้รับข้อความคอมมิตสำหรับการนำทางและเริ่มรับข้อมูล HTML เทรดหลักจะเริ่มแยกวิเคราะห์สตริงข้อความ (HTML) และเปลี่ยนให้เป็น มิติข้อมูลของโม (DOM)
DOM คือการแสดงหน้าเว็บภายในของเบราว์เซอร์ รวมถึงโครงสร้างข้อมูลและ API ที่นักพัฒนาเว็บโต้ตอบด้วยผ่าน JavaScript ได้
การแยกวิเคราะห์เอกสาร HTML เป็น DOM จะเป็นไปตามมาตรฐาน HTML คุณอาจสังเกตเห็นว่าการป้อน HTML ในเบราว์เซอร์
ไม่แสดงข้อผิดพลาด เช่น ไม่มีแท็กปิด </p>
คือ HTML ที่ถูกต้อง ระบบจะดำเนินการกับมาร์กอัปที่ไม่ถูกต้อง เช่น Hi! <b>I'm <i>Chrome</b>!</i>
(แท็ก b ปิดก่อนแท็ก i) เหมือนกับว่าคุณเขียน Hi! <b>I'm <i>Chrome</i></b><i>!</i>
เนื่องจากข้อกำหนด HTML ได้รับการออกแบบมาเพื่อจัดการกับข้อผิดพลาดเหล่านี้ได้อย่างสง่างาม หากคุณมีข้อสงสัยว่าต้องทำอย่างไร ให้อ่านต่อในส่วน "ข้อมูลเบื้องต้นเกี่ยวกับการจัดการข้อผิดพลาดและกรณีแปลกๆ ในโปรแกรมแยกวิเคราะห์" ของข้อมูลจำเพาะ HTML
กำลังโหลดทรัพยากรย่อย
โดยปกติแล้วเว็บไซต์จะใช้แหล่งข้อมูลภายนอก เช่น รูปภาพ, CSS และ JavaScript ไฟล์เหล่านั้นต้องโหลด
จากเครือข่ายหรือแคช โดยเทรดหลักอาจส่งคำขอทีละรายการเมื่อพบในระหว่างที่แยกวิเคราะห์เพื่อสร้าง DOM แต่ระบบจะเรียกใช้ "โหลดเครื่องสแกนล่วงหน้า" พร้อมกันเพื่อให้ทำงานได้เร็วขึ้น
หากมี <img>
หรือ <link>
ในเอกสาร HTML ให้โหลดเครื่องสแกนล่วงหน้าเพื่อดูโทเค็นที่สร้างโดยโปรแกรมแยกวิเคราะห์ HTML และส่งคำขอไปยังเทรดเครือข่ายในกระบวนการของเบราว์เซอร์
JavaScript สามารถบล็อกการแยกวิเคราะห์
เมื่อโปรแกรมแยกวิเคราะห์ HTML พบแท็ก <script>
โปรแกรมจะหยุดการแยกวิเคราะห์เอกสาร HTML ไว้ชั่วคราว และจะต้องโหลด แยกวิเคราะห์ และเรียกใช้โค้ด JavaScript นั่นเป็นเพราะว่า JavaScript สามารถเปลี่ยนรูปร่างของเอกสารโดยใช้สิ่งต่างๆ อย่างเช่น document.write()
ซึ่งเปลี่ยนแปลงโครงสร้าง DOM ทั้งหมด (ภาพรวมของโมเดลการแยกวิเคราะห์ในข้อมูลจำเพาะของ HTML มีแผนภาพที่สวยงาม) ด้วยเหตุนี้ โปรแกรมแยกวิเคราะห์ HTML จะต้องรอให้ JavaScript ทำงานก่อนที่จะกลับมาแยกวิเคราะห์เอกสาร HTML ได้ หากคุณมีข้อสงสัยเกี่ยวกับสิ่งที่เกิดขึ้นในการเรียกใช้ JavaScript ทีม V8 ได้เข้ามาพูดคุยและบล็อกโพสต์เกี่ยวกับเรื่องนี้
คำแนะนำให้เบราว์เซอร์บอกวิธีโหลดทรัพยากร
มีหลายวิธีที่นักพัฒนาเว็บสามารถส่งคำแนะนำไปยังเบราว์เซอร์เพื่อที่จะโหลดทรัพยากรต่างๆ อย่างเหมาะสม
หาก JavaScript ไม่ได้ใช้ document.write()
คุณสามารถเพิ่มแอตทริบิวต์ async
หรือ defer
ลงในแท็ก <script>
ได้ จากนั้นเบราว์เซอร์จะโหลดและเรียกใช้โค้ด JavaScript แบบไม่พร้อมกันและไม่บล็อกการแยกวิเคราะห์ นอกจากนี้คุณยังใช้โมดูล JavaScript ได้ตามความเหมาะสม <link rel="preload">
เป็นช่องทางในการแจ้งให้เบราว์เซอร์ทราบว่าจำเป็นต้องใช้ทรัพยากรสำหรับการนำทางในปัจจุบันอย่างแน่นอน และคุณต้องการดาวน์โหลดโดยเร็วที่สุด อ่านข้อมูลเพิ่มเติมได้ที่การจัดลำดับความสำคัญของทรัพยากร – ให้เบราว์เซอร์ช่วยคุณ
การคำนวณรูปแบบ
การมี DOM นั้นไม่เพียงพอที่จะรู้ว่าหน้าจะมีลักษณะอย่างไรเพราะเราสามารถจัดรูปแบบองค์ประกอบของหน้าใน CSS ได้ เทรดหลักจะแยกวิเคราะห์ CSS และกำหนดรูปแบบที่คำนวณสำหรับโหนด DOM แต่ละรายการ นี่เป็นข้อมูลเกี่ยวกับประเภทของสไตล์ที่ใช้กับองค์ประกอบแต่ละรายการโดยอิงตามตัวเลือก CSS คุณดูข้อมูลนี้ได้ในส่วน computed
ของเครื่องมือสำหรับนักพัฒนาเว็บ
แม้ว่าคุณจะไม่ได้ระบุ CSS เลย แต่โหนด DOM แต่ละรายการก็จะมีรูปแบบที่คำนวณได้ แท็ก <h1>
แสดง
ใหญ่กว่าแท็ก <h2>
และมีการกำหนดระยะขอบสำหรับแต่ละองค์ประกอบ เนื่องจากเบราว์เซอร์มี
สไตล์ชีตเริ่มต้น หากต้องการทราบว่า CSS เริ่มต้นของ Chrome เป็นอย่างไร
คุณสามารถดูซอร์สโค้ดได้ที่นี่
เลย์เอาต์
ตอนนี้กระบวนการแสดงผลรู้ถึงโครงสร้างของเอกสารและรูปแบบสำหรับแต่ละโหนดแล้ว แต่นั่นยังไม่เพียงพอในการแสดงผลหน้าเว็บ สมมติว่าคุณกำลังพยายามอธิบายภาพวาดให้เพื่อนฟังผ่านโทรศัพท์ "มีวงกลมสีแดงขนาดใหญ่และสี่เหลี่ยมจัตุรัสเล็กสีน้ำเงิน" จึงเป็นข้อมูลไม่เพียงพอที่จะทำให้เพื่อนของคุณทราบว่าภาพวาดมีหน้าตาเป็นอย่างไร
เลย์เอาต์เป็นกระบวนการหาเรขาคณิตขององค์ประกอบ เทรดหลักจะสำรวจ DOM และรูปแบบที่คำนวณ และสร้างแผนผังเลย์เอาต์ที่มีข้อมูล เช่น พิกัด x y และขนาดกล่องล้อมรอบ แผนผังเลย์เอาต์อาจมีโครงสร้างคล้ายกับแผนผัง DOM แต่มีเฉพาะข้อมูลที่เกี่ยวข้องกับสิ่งที่เห็นในหน้าเว็บเท่านั้น หากใช้ display: none
องค์ประกอบดังกล่าวจะไม่เป็นส่วนหนึ่งของแผนผังเลย์เอาต์ (แต่องค์ประกอบที่มี visibility: hidden
จะอยู่ในแผนผังเลย์เอาต์) ในทำนองเดียวกัน หากใช้คลาสจำลองที่มีเนื้อหาอย่าง p::before{content:"Hi!"}
ระบบจะรวมคลาสดังกล่าวไว้ในโครงสร้างเลย์เอาต์แม้ว่าจะไม่อยู่ใน DOM ก็ตาม
การพิจารณาเลย์เอาต์ของหน้าเว็บเป็นงานที่ท้าทาย แม้แต่เลย์เอาต์หน้าเว็บที่ง่ายที่สุดอย่างโฟลว์บล็อกจากบนลงล่างก็ต้องพิจารณาขนาดของแบบอักษรและตำแหน่งที่จะขึ้นบรรทัดใหม่ เพราะองค์ประกอบเหล่านี้ส่งผลต่อขนาดและรูปร่างของย่อหน้า ซึ่งจะส่งผลต่อตำแหน่งของย่อหน้าต่อไปนี้ด้วย
CSS สามารถทำให้องค์ประกอบลอยไปด้านหนึ่ง มาสก์รายการส่วนเกิน และเปลี่ยนทิศทางการเขียนได้ สมมติว่าพื้นที่งานของเลย์เอาต์นี้มีงานที่หนักมาก ทีมวิศวกรทุกคนใน Chrome จะทำงานเกี่ยวกับการออกแบบ หากคุณต้องการดูรายละเอียดเกี่ยวกับงานของพวกเขา การพูดคุยสองสามครั้งจาก BlinkOn Conference จะได้รับการบันทึกไว้และน่าสนใจทีเดียว
สี
การมี DOM, รูปแบบ และเลย์เอาต์ยังไม่เพียงพอต่อการแสดงผลหน้าเว็บ สมมติว่าคุณพยายาม จำลองภาพวาด คุณทราบขนาด รูปร่าง และตำแหน่งขององค์ประกอบได้แล้ว แต่ยังต้องตัดสินใจว่าต้องเรียงลำดับอะไรในการทาสี
เช่น อาจมีการตั้งค่า z-index
สำหรับองค์ประกอบบางอย่าง ในกรณีนี้การวาดภาพตามลำดับองค์ประกอบที่เขียนใน HTML จะทำให้การแสดงผลไม่ถูกต้อง
ในขั้นตอนการแสดงผลนี้ เทรดหลักจะเดินไปตามแผนผังเลย์เอาต์เพื่อสร้างระเบียนสี ระเบียนการแสดงผลคือ
ขั้นตอนการวาดภาพอย่าง "พื้นหลังก่อน ตามด้วยข้อความ แล้วสี่เหลี่ยมผืนผ้า" หากวาดองค์ประกอบ <canvas>
โดยใช้ JavaScript คุณก็อาจคุ้นเคยกับขั้นตอนนี้
การอัปเดตไปป์ไลน์การแสดงผลมีค่าใช้จ่ายสูง
สิ่งสำคัญที่สุดที่ต้องทำความเข้าใจเกี่ยวกับไปป์ไลน์การแสดงผลคือ ในแต่ละขั้นตอน ผลของการดำเนินการก่อนหน้าจะถูกนำมาใช้เพื่อสร้างข้อมูลใหม่ เช่น ถ้าแผนผังของเลย์เอาต์มีการเปลี่ยนแปลง ก็ต้องสร้างลำดับการระบายสีใหม่สำหรับส่วนที่ได้รับผลกระทบของเอกสาร
หากคุณกำลังทำให้องค์ประกอบเคลื่อนไหว เบราว์เซอร์จะต้องเรียกใช้การดำเนินการเหล่านี้ระหว่างทุกเฟรม จอแสดงผลส่วนใหญ่จะรีเฟรชหน้าจอ 60 ครั้งต่อวินาที (60 fps) ภาพเคลื่อนไหวจะแสดงอย่างราบรื่นต่อสายตามนุษย์เมื่อคุณย้ายสิ่งต่างๆ บนหน้าจอทุกเฟรม แต่หากภาพเคลื่อนไหวไม่มีเฟรมที่อยู่ตรงกลาง หน้าเว็บจะแสดงว่า "ไม่ดี"
แม้ว่าการดำเนินการแสดงผลของคุณจะเป็นไปตามการรีเฟรชหน้าจอ แต่การคำนวณเหล่านี้จะทำงานในเทรดหลัก ซึ่งหมายความว่าอาจมีการบล็อกเมื่อแอปพลิเคชันของคุณเรียกใช้ JavaScript
คุณสามารถแบ่งการดำเนินการ JavaScript ออกเป็นส่วนเล็กๆ และกำหนดเวลาให้ทำงานที่ทุกเฟรมได้โดยใช้ requestAnimationFrame()
ดูข้อมูลเพิ่มเติมเกี่ยวกับหัวข้อนี้ได้ที่เพิ่มประสิทธิภาพการเรียกใช้ JavaScript นอกจากนี้คุณยังสามารถเรียกใช้ JavaScript ใน Web Worker เพื่อหลีกเลี่ยงการบล็อกเทรดหลัก
การประกอบ
คุณจะวาดหน้าเว็บอย่างไร
ตอนนี้เบราว์เซอร์รู้โครงสร้างของเอกสาร รูปแบบขององค์ประกอบแต่ละรายการ เรขาคณิตของหน้า และลำดับสีแล้ว เบราว์เซอร์จะวาดหน้าได้อย่างไร การเปลี่ยนข้อมูลนี้เป็นพิกเซลบนหน้าจอ เรียกว่าการแรสเตอร์
วิธีที่ง่ายในการจัดการปัญหานี้อาจเป็นการแรสเตอร์ส่วนต่างๆ ภายในวิวพอร์ต หากผู้ใช้เลื่อนหน้าเว็บ ให้ย้ายเฟรมแรสเตอร์และเติมเต็มส่วนที่ขาดไปด้วยการแรสเตอร์เพิ่ม นี่เป็นวิธีที่ Chrome จัดการกับการแรสเตอร์เมื่อเผยแพร่ครั้งแรก แต่เบราว์เซอร์ที่ทันสมัย จะดำเนินการที่ซับซ้อนมากขึ้นที่เรียกว่า การประกอบ (Compositing)
การประกอบคืออะไร
การจัดองค์ประกอบเป็นเทคนิคในการแยกส่วนต่างๆ ของหน้าออกเป็นเลเยอร์ แรสเตอร์ส่วนต่างๆ แยกกัน และประกอบเป็นหน้าในเทรดที่แยกต่างหากที่เรียกว่าเทรดคอมโพสิเตอร์ หากการเลื่อนเกิดขึ้นเนื่องจากเลเยอร์มีการแรสเตอร์อยู่แล้ว สิ่งที่ต้องทำก็คือการผสมเฟรมใหม่ ภาพเคลื่อนไหวจะทำได้ด้วยวิธีเดียวกันนี้ด้วยการย้ายเลเยอร์และประกอบเฟรมใหม่
คุณดูวิธีแบ่งเว็บไซต์ออกเป็นเลเยอร์ต่างๆ ได้ในเครื่องมือสำหรับนักพัฒนาเว็บโดยใช้แผงเลเยอร์
การแบ่งออกเป็นเลเยอร์
ในการตรวจสอบว่าองค์ประกอบใดต้องอยู่ในเลเยอร์ใด เทรดหลักจะเดินผ่านแผนผังเลย์เอาต์เพื่อสร้างแผนผังเลเยอร์ (ส่วนนี้เรียกว่า "Update Layer Tree" ในแผงประสิทธิภาพของเครื่องมือสำหรับนักพัฒนาเว็บ) หากไม่มีบางส่วนของหน้าที่ควรเป็นเลเยอร์แยกกัน (เช่น เมนูด้านข้างแบบเลื่อนเข้า) คุณอาจให้คำแนะนำแก่เบราว์เซอร์โดยใช้แอตทริบิวต์ will-change
ใน CSS
คุณอาจจะอยากสร้างเลเยอร์กับทุกองค์ประกอบ แต่การประกอบเลเยอร์ที่มากเกินไปอาจทำให้การทำงานช้าลงกว่าการแรสเตอร์ส่วนเล็กๆ ของหน้าเว็บทุกเฟรม การวัดประสิทธิภาพในการแสดงผลของแอปพลิเคชันจึงมีความสำคัญมาก ดูข้อมูลเพิ่มเติมเกี่ยวกับหัวข้อนี้ได้ที่ยึดพร็อพเพอร์ตี้เฉพาะคอมโพเนนต์เท่านั้นและจัดการจำนวนเลเยอร์
แรสเตอร์และคอมโพสิตจากเทรดหลัก
เมื่อสร้างแผนผังเลเยอร์และกำหนดลำดับสีแล้ว เทรดหลักจะส่งข้อมูลดังกล่าวไปยังเทรดคอมโพสิเตอร์ จากนั้นเทรดคอมโพสิเตอร์จะแรสเตอร์แต่ละเลเยอร์ เลเยอร์อาจมีขนาดใหญ่เท่าความยาวทั้งหมดของหน้าเว็บ ดังนั้นเทรดคอมโพเนนต์จะแบ่งชิ้นส่วนออกเป็นชิ้นส่วนย่อย แล้วส่งแต่ละชิ้นส่วนไปยังชุดข้อความแรสเตอร์ เทรดแรสเตอร์จะแรสเตอร์แต่ละการ์ดและจัดเก็บไว้ในหน่วยความจำ GPU
เทรดคอมโพสิเตอร์จะจัดลำดับความสำคัญเทรดแรสเตอร์ต่างๆ เพื่อให้ระบบแรสเตอร์สิ่งต่างๆ ภายในวิวพอร์ต (หรือใกล้เคียง) ก่อน นอกจากนี้ เลเยอร์ยังมีการเรียงชิดกันหลายแบบสำหรับความละเอียดต่างๆ เพื่อจัดการกับสิ่งต่างๆ เช่น การซูมเข้า
เมื่อการ์ดมีการแรสเตอร์ ชุดข้อความคอมโพสิเตอร์จะรวบรวมข้อมูลชิ้นส่วนที่เรียกว่าการวาดรูปสี่เหลี่ยมเพื่อสร้างเฟรมองค์ประกอบ
วาดสี่เหลี่ยม | มีข้อมูล เช่น ตำแหน่งของชิ้นส่วนในหน่วยความจำ และตำแหน่งในหน้าสำหรับวาดการ์ดโดยคำนึงถึงการประกอบหน้ากระดาษ |
เฟรมวัสดุ | คอลเล็กชันของสี่เหลี่ยมที่ใช้วาดซึ่งแสดงถึงเฟรมของหน้าเว็บ |
จากนั้นระบบจะส่งเฟรมอุปกรณ์ประกอบไปยังกระบวนการของเบราว์เซอร์ผ่าน IPC เมื่อถึงจุดนี้ คุณสามารถเพิ่มเฟรมตัวประกอบอื่นจากเธรด UI สำหรับการเปลี่ยนแปลง UI ของเบราว์เซอร์ หรือจากกระบวนการแสดงผลอื่นๆ สำหรับส่วนขยายได้ ระบบจะส่งเฟรมคอมโพสิเตอร์เหล่านี้ไปยัง GPU เพื่อแสดงบนหน้าจอ หากมีเหตุการณ์การเลื่อนเข้ามา เทรดคอมโพสิเตอร์จะสร้างเฟรมตัวประมวลผลอีกเฟรมเพื่อส่งไปยัง GPU
ประโยชน์ของการประกอบภาพคือทำได้โดยไม่ต้องเกี่ยวข้องกับเทรดหลัก เทรดแบบผสมไม่จำเป็นต้องรอการคำนวณสไตล์หรือการเรียกใช้ JavaScript ด้วยเหตุนี้ การประกอบเฉพาะภาพเคลื่อนไหวจึงถือว่าดีที่สุดเพื่อประสิทธิภาพที่ราบรื่น หากต้องมีการคำนวณเลย์เอาต์หรือสีอีกครั้ง ก็จะต้องมีเทรดหลักเข้ามาเกี่ยวข้อง
สรุป
ในโพสต์นี้ เราพิจารณาการแสดงผลไปป์ไลน์ ตั้งแต่การแยกวิเคราะห์ไปจนถึงการประมวลผลภาพ หวังว่าตอนนี้คุณมีขีดความสามารถ ที่จะอ่านเพิ่มเติมเกี่ยวกับการเพิ่มประสิทธิภาพของเว็บไซต์แล้ว
ในโพสต์ถัดไปและสุดท้ายของชุดนี้ เราจะดูรายละเอียดเพิ่มเติมเกี่ยวกับชุดข้อความของตัวจัดวาง และดูว่าจะเกิดอะไรขึ้นเมื่อมีการป้อนข้อมูลของผู้ใช้ เช่น mouse move
และ click
คุณชอบโพสต์นี้ไหม หากคุณมีคำถามหรือคำแนะนำสำหรับโพสต์ในอนาคต เรายินดีรับฟังคุณในส่วนความคิดเห็นด้านล่างหรือ @kosamari บน Twitter