สวัสดีชาว Datafarm และ FC Datafarm ครับ
สำหรับสัปดาห์นี้ จากที่เห็นทั้งเพื่อนในออฟฟิศ นอกออฟฟิศ คิดว่าหลายๆ คนคงวุ่นกับการทำ report หรือเร่งปิด ticket ปั่น commit กันให้วุ่น คงจะไม่ดีแน่ๆ ถ้าตอนวุ่นๆแล้วมี ad-hoc เด้งเข้ามา
ซึ่งบทความในสัปดาห์นี้ก็ถือเป็น ad-hoc สำหรับแอดมินเองด้วยเช่นกัน
(สำหรับวันนี้แอดขอลงเป็นบทความเอาใจฝั่ง Developer บ้าง แนวๆ Dev101 หลังจากก่อนหน้านี้เราหมกมุ่นไปทาง Cybersecurity ซะส่วนใหญ่)
เกริ่นคร่าวๆ เมื่อเร็วๆนี้ แอดมินเองได้รับมอบหมายให้ทำงาน frontend งานนึง ซึ่งไม่มี features อะไรมาก “แค่แสดงข้อมูลเท่านั้น” ซึ่งผลลัพธ์สุดท้ายไม่พ้นไฟล์ HTML CSS JS ที่โยนมาจากที่ไหนซักแห่งมาให้ browser ของเรา แล้วแสดงผลออกมาให้เห็นหรือสามารถทำการ interact กับ user ได้ด้วย
ฟังแล้วดูตรงไปตรงมา เรียบง่าย แต่จากประสบการณ์ที่ผ่านๆ มา ก็มี challenges หลายอย่างที่ต้องทำและต้องรับมือ (ไม่ใช่ “แค่” แสดงข้อมูลเฉยๆ แล้วจบ) อาทิ เช่น
user ต้องเห็นเว็ปไซต์ภายใน 1 วินาที หลังจากกดเข้าเว็ปไซต์
เว็ปไซต์ต้องสมูธ มี FPS อยู่ที่ 60FPS
อย่าให้มี Ugly UI แม้แต่เสี้ยววิเดียว
ตัวอย่างที่กล่าวมามันเกี่ยวกับเรื่อง rendering performance ซึ่งสิ่งนี้แหละ ที่เราจะมาทำความรู้จักกับมันแบบคร่าวๆในบทความนี้
รู้จักกับ The pixel pipeline
ในการที่จะแสดง pixel บน screen ในช่วงเวลานึงนั้นมีขั้นตอนค่อนข้างเยอะพอสมควร ตัวอย่างเช่น ผู้ใช้กดปุ่มขวาบนใน Gmail เพื่อจะเปลี่ยนไปอีเมล์อื่น
สิ่งที่เกิดคือสั่ง js ถูกสั่งให้เปลี่ยนแปลงในระดับ visual, มีการเปลี่ยนแปลงของ DOM และทำการ match selector ใหม่, คำนวนขนาด element ใหม่, วาดสิ่งที่คำนวนได้ในรูปแบบ pixel และนำทุกสิ่งที่ถูกสร้างมาประกอบจนกลายเป็นผลลัพธ์
ซึ่งในที่นี้คือ แถบเมนูที่ถูกขยายออกมาตามภาพ
จากที่อธิบายมามันยังเป็นแค่ภาพรวม เราจะมาดูองค์ประกอบใน pipeline ทีละตัวกันครับ
Javascript / CSS
เป็นฟังก์ชั่นสำหรับ trigger การเกิด visual changes ต่างๆ เป็นต้นทางของการเกิดการเปลี่ยนแปลงต่างๆใน 1 frame ของ pipeline
ตัวอย่างฟังก์ชั่น visual changes ได้แก่ setTimeout, setInterval และ requestAnimationFrame แน่นอนการเกิด visual changes ไม่จำเป็นต้องเกิดจาก js เพียงอย่างเดียว สามารถเรียกใช้ผ่าน CSS Animations, Transitions และ Web Animations API ได้ด้วยนะ
Style
สิ่งที่เกิดขึ้นภายในฟังก์ชั่น visual changes ไม่ว่าจะเป็นการเปลี่ยนแปลง DOM เพิ่ม/ลบ attributes หรือ classes จะทำให้เกิดสิ่งที่เรียกว่า Style calculations คือการสร้าง set ของ selector ทั้งหลายทั้งปวง และนำมา apply ให้กับ “element ทุกตัว”
ผลลัพธ์สุดท้ายคือ browser จะรู้ว่า element แต่ละตัวมี style อะไรบ้าง
Layout
หลังจากที่รู้ว่า element ไหนมี style/rule เป็นอะไรบ้าง browser จะเริ่มคำนวณพื้นสิ่งที่จะถูกวาดออกมา อาจคำนวณได้จาก selector บางตัว, content ใน element นั้นๆ เช่นตัวหนังสือ (แต่ยังไม่วาดนะ) หรืออาจเป็นพื้นที่ของ parent element ก็ได้
ตัวอย่าง selector ที่จะถูกนำมาคำนวนในขั้นตอนนี้ หรือสามารถ trigger ขั้นตอนนี้ได้ width,height,padding,margin,display,border,position ฯลฯ
Paint
ขั้นตอนนี้เปรียบเสมือนการลงสี เมื่อ browser ได้พื้นที่ของสิ่งต่างๆที่กำลังจะมาโชว์ในหน้าจอแล้ว สิ่งที่เกิดขึ้นคือสร้าง pixel ที่จะกลายเป็น content ต่างๆ เช่น ตัวหนังสือ รูปภาพ รวมไปถึง selector ส่วนใหญ่ เช่น color, background-color, borders ฯลฯ ผลลัพธ์ของขั้นตอนนี้อาจถูกวาดออกมาได้เป็นหลาย layer หรือ layer เดียวก็ได้
Compositing
เมื่อเราได้ layer ที่วาด content มาเสร็จสรรพแล้ว ขั้นตอนสุดท้ายคือการนำสิ่งเหล่านี้มาแสดงบน screen โดย “การนำมาประกอบตามลำดับที่ถูกต้อง”
ตัวอย่างง่ายๆเหมือนการแบ่ง layer ในโปรแกรม Adobe PhotoShop แล้วเรียงหน้าหลังให้ถูกต้องแค่นั้นแหละ
สำหรับ selector ที่สามารถ trigger ขั้นตอนนี้โดยตรง โดยไม่ต้องผ่านขั้นตอน Layout และ Paint มีแค่ 2 ตัวได้แก่ transform และ opacity (อ้างอิงจาก developers.google.com)
แต่มี developer บางคนบอกว่าการทำ z-index ก็สามารถ trigger ได้โดยตรง ซึ่งแอดมินเองยังไม่มีเวลาลอง แต่ถ้าว่าด้วย behavior ของขั้นตอนนี้ z-index มันคือการเรียง layer ดีๆนั่นแหละ
จะเห็นได้ว่าใน 1 เฟรมของ Pixel Pipeline สามารถ trigger ทั้ง 5 องค์ประกอบหรือน้อยกว่านั้นก็ได้ เราสามารถสรุปออกมาได้ดังนี้
- JS / CSS > Style > Layout > Paint > Composite
เป็นการ trigger ในระดับ Layout โดยการเปลี่ยนแปลงรูปทรงของ Element ใดๆ หลังจากนั้นจะทำการ Repainted และ Composite กลับต่อ สังเกตได้ว่าการ trigger ในระดับนี้มี impact กับทุก element การ trigger ที่ไม่ดีอาจทำให้เห็น UI ที่ยืดหดโดยไม่มีความจำเป็น
2. JS / CSS > Style > Paint > Composite
หากเราต้องการ render พวกที่วาดออกมาเป็น pixel อาทิ เช่น background-image, text, color หรือ shadow เราสามารถ trigger ในระดับ Paint ได้โดยตรง ไม่จำเป็นต้องผ่านระดับ Layout
3. JS / CSS > Style > Composite
สำหรับการ trigger ในระดับนี้จะข้ามระดับ Layout และ Paint ไป ทำให้เปลืองทรัพยากรน้อยที่สุด เหมาะสำหรับแอพที่มีการใช้ animation
จะทราบได้อย่างไรว่า Selector ตัวไหน Trigger ในระดับไหน ?
สามารถดูได้จากลิสตามลิ้งข้างล่างได้เลยครับ
https://csstriggers.com/
https://docs.google.com/spreadsheets/d/1Hvi0nu2wG3oQ51XRHtMv-A_ZlidnwUYwgQsPQUg1R2s/pub?single=true&gid=0&output=html
เพื่อความชัดเจน เราสามารถเปิด Dev tool เพื่อดูให้ละเอียดมากขึ้นได้ อันนี้แอดมินเอง highly recommend เลยครับ
สรุปการทำ rendering performance อาจเป็น non-functional เพราะมี cost ที่ไม่น้อย แค่การทำให้มันเวิคให้ทันเวลาก็ยากแล้ว ไหนจะแก้บัคอีก แต่การที่ไม่วางโครงเลย การย้อนกลับมาทำอาจยากกว่าการเขียนโครงใหม่ก็ได้ ลองวางแผนกันดูครับ สำหรับมือใหม่ถือเป็นการเริ่มต้นที่ดีมาก และที่สำคัญที่สุดความสำเร็จ ไม่ใช่แค่แอพทำงานได้ดี, สมูธ, ไร้บัค หรือ ไม่สร้างภาระให้เครื่องผู้ใช้ อย่าลืมนะครับว่าสิ่งที่ render ออกไปให้ผู้ใช้เห็นต้องเป็นสิ่งที่ “ปลอดภัย” ด้วยครับ อันนี้สำคัญมาก… ห้ า ม ลื ม เ ด็ ด ข า ด
หมดเวลาสำหรับบทความรอบนี้แล้ว ถ้ามีข้อติเตียนหรือคำแนะนำ สามารถแจ้งมาได้ เลยนะครับ
สำหรับสัปดาห์นี้แอดมินเองขอตัวก่อนครับ สวัสดีครับ