เชี่ยวชาญประสิทธิภาพเว็บโดยการวิเคราะห์และเพิ่มประสิทธิภาพ Critical Rendering Path คู่มือสำหรับนักพัฒนาเพื่อทำความเข้าใจและแก้ไขผลกระทบของ JavaScript ต่อการเรนเดอร์
การเพิ่มประสิทธิภาพ JavaScript: เจาะลึก Critical Rendering Path
ในโลกของการพัฒนาเว็บ ความเร็วไม่ใช่แค่ฟีเจอร์ แต่เป็นรากฐานของประสบการณ์ผู้ใช้ที่ดี เว็บไซต์ที่โหลดช้าอาจนำไปสู่ bounce rates ที่สูงขึ้น, conversion ที่ลดลง และผู้ใช้ที่หงุดหงิด แม้ว่าจะมีปัจจัยหลายอย่างที่ส่งผลต่อประสิทธิภาพของเว็บ แต่หนึ่งในแนวคิดพื้นฐานที่สุดและมักถูกเข้าใจผิดคือ Critical Rendering Path (CRP) การทำความเข้าใจว่าเบราว์เซอร์เรนเดอร์เนื้อหาอย่างไร และที่สำคัญกว่านั้นคือ JavaScript มีปฏิสัมพันธ์กับกระบวนการนี้อย่างไร ถือเป็นสิ่งสำคัญยิ่งสำหรับนักพัฒนาที่จริงจังกับเรื่องประสิทธิภาพ
คู่มือฉบับสมบูรณ์นี้จะพาคุณเจาะลึกเข้าไปใน Critical Rendering Path โดยเน้นเฉพาะบทบาทของ JavaScript เราจะสำรวจวิธีการวิเคราะห์, ระบุปัญหาคอขวด และใช้เทคนิคการเพิ่มประสิทธิภาพอันทรงพลังที่จะทำให้เว็บแอปพลิเคชันของคุณเร็วขึ้นและตอบสนองได้ดีขึ้นสำหรับฐานผู้ใช้ทั่วโลก
Critical Rendering Path คืออะไร?
Critical Rendering Path คือลำดับขั้นตอนที่เบราว์เซอร์ต้องทำเพื่อแปลง HTML, CSS และ JavaScript ให้กลายเป็นพิกเซลที่มองเห็นได้บนหน้าจอ เป้าหมายหลักของการเพิ่มประสิทธิภาพ CRP คือการเรนเดอร์เนื้อหาเริ่มต้นที่อยู่ 'เหนือขอบล่างของจอ' (above-the-fold) ให้กับผู้ใช้โดยเร็วที่สุด ยิ่งขั้นตอนนี้เกิดขึ้นเร็วเท่าไหร่ ผู้ใช้ก็จะรับรู้ว่าหน้าเว็บโหลดเร็วขึ้นเท่านั้น
เส้นทางนี้ประกอบด้วยขั้นตอนสำคัญหลายขั้นตอน:
- การสร้าง DOM: กระบวนการเริ่มต้นเมื่อเบราว์เซอร์ได้รับไบต์แรกของเอกสาร HTML จากเซิร์ฟเวอร์ มันจะเริ่มแยกวิเคราะห์ (parsing) โค้ด HTML ทีละตัวอักษร และสร้าง Document Object Model (DOM) ขึ้นมา DOM เป็นโครงสร้างแบบต้นไม้ที่แสดงโหนดทั้งหมด (องค์ประกอบ, แอตทริบิวต์, ข้อความ) ในเอกสาร HTML
- การสร้าง CSSOM: ขณะที่เบราว์เซอร์สร้าง DOM หากพบสไตล์ชีต CSS (ไม่ว่าจะในแท็ก
<link>หรือบล็อก<style>แบบอินไลน์) มันจะเริ่มสร้าง CSS Object Model (CSSOM) ขึ้นมา เช่นเดียวกับ DOM, CSSOM เป็นโครงสร้างแบบต้นไม้ที่ประกอบด้วยสไตล์ทั้งหมดและความสัมพันธ์ของสไตล์เหล่านั้นสำหรับหน้าเว็บ แต่ต่างจาก HTML, CSS นั้นเป็นแบบ render-blocking (บล็อกการเรนเดอร์) โดยค่าเริ่มต้น เบราว์เซอร์ไม่สามารถเรนเดอร์ส่วนใดๆ ของหน้าเว็บได้จนกว่าจะดาวน์โหลดและแยกวิเคราะห์ CSS ทั้งหมดเสร็จสิ้น เนื่องจากสไตล์ที่มาทีหลังอาจจะเขียนทับสไตล์ที่มาก่อนหน้า - การสร้าง Render Tree: เมื่อทั้ง DOM และ CSSOM พร้อมแล้ว เบราว์เซอร์จะนำทั้งสองอย่างมารวมกันเพื่อสร้าง Render Tree ขึ้นมา ทรีนี้จะประกอบด้วยโหนดที่จำเป็นต่อการเรนเดอร์หน้าเว็บเท่านั้น ตัวอย่างเช่น องค์ประกอบที่มี
display: none;และแท็ก<head>จะไม่ถูกรวมอยู่ใน Render Tree เพราะพวกมันไม่ได้ถูกเรนเดอร์ออกมาเป็นภาพ Render Tree จะรู้ว่าต้องแสดงอะไร แต่ไม่รู้ว่าจะแสดงที่ไหนหรือขนาดเท่าไหร่ - Layout (หรือ Reflow): เมื่อสร้าง Render Tree เสร็จแล้ว เบราว์เซอร์จะเข้าสู่ขั้นตอน Layout ในขั้นตอนนี้ มันจะคำนวณขนาดและตำแหน่งที่แน่นอนของแต่ละโหนดใน Render Tree เทียบกับ viewport ผลลัพธ์ของขั้นตอนนี้คือ 'box model' ที่ระบุรูปทรงเรขาคณิตที่แม่นยำของทุกองค์ประกอบบนหน้าเว็บ
- Paint: ในที่สุด เบราว์เซอร์จะนำข้อมูล layout มา 'วาด' พิกเซลสำหรับแต่ละโหนดลงบนหน้าจอ ซึ่งรวมถึงการวาดข้อความ, สี, รูปภาพ, เส้นขอบ และเงา โดยพื้นฐานแล้วคือการแรสเตอร์ (rasterizing) ทุกส่วนของหน้าเว็บที่มองเห็นได้ กระบวนการนี้อาจเกิดขึ้นบนหลายเลเยอร์เพื่อเพิ่มประสิทธิภาพ
- Composite: หากเนื้อหาของหน้าเว็บถูกวาดลงบนหลายเลเยอร์ เบราว์เซอร์จะต้องนำเลเยอร์เหล่านี้มาประกอบกันตามลำดับที่ถูกต้องเพื่อแสดงผลภาพสุดท้ายบนหน้าจอ ขั้นตอนนี้มีความสำคัญอย่างยิ่งสำหรับแอนิเมชันและการเลื่อนหน้าจอ เนื่องจากการ composite โดยทั่วไปใช้พลังการคำนวณน้อยกว่าการรันขั้นตอน Layout และ Paint ใหม่ทั้งหมด
บทบาทของ JavaScript ที่ขัดขวาง Critical Rendering Path
แล้ว JavaScript เข้ามามีบทบาทตรงไหน? JavaScript เป็นภาษาที่ทรงพลังที่สามารถแก้ไขได้ทั้ง DOM และ CSSOM อย่างไรก็ตาม พลังนี้ก็มีราคาที่ต้องจ่าย JavaScript สามารถ และมักจะ บล็อก Critical Rendering Path ซึ่งนำไปสู่ความล่าช้าอย่างมากในการเรนเดอร์
JavaScript ที่บล็อก Parser
โดยค่าเริ่มต้น JavaScript เป็นแบบ parser-blocking (บล็อกการแยกวิเคราะห์) เมื่อ HTML parser ของเบราว์เซอร์เจอแท็ก <script> มันจะต้องหยุดกระบวนการสร้าง DOM ชั่วคราว จากนั้นจะทำการดาวน์โหลด (หากเป็นไฟล์ภายนอก), แยกวิเคราะห์ และรันไฟล์ JavaScript กระบวนการนี้เป็นการบล็อกเพราะสคริปต์อาจทำบางอย่างเช่น document.write() ซึ่งสามารถเปลี่ยนแปลงโครงสร้าง DOM ทั้งหมดได้ เบราว์เซอร์ไม่มีทางเลือกอื่นนอกจากต้องรอให้สคริปต์ทำงานเสร็จก่อนจึงจะสามารถกลับมาแยกวิเคราะห์ HTML ต่อได้อย่างปลอดภัย
หากสคริปต์นี้อยู่ในส่วน <head> ของเอกสารของคุณ มันจะบล็อกการสร้าง DOM ตั้งแต่เริ่มต้น ซึ่งหมายความว่าเบราว์เซอร์ไม่มีเนื้อหาที่จะเรนเดอร์ และผู้ใช้จะต้องจ้องมองหน้าจอขาวๆ ไปจนกว่าสคริปต์จะถูกประมวลผลเสร็จสิ้น นี่คือสาเหตุหลักของประสิทธิภาพที่รับรู้ได้ว่าไม่ดี
การจัดการ DOM และ CSSOM
JavaScript ยังสามารถสืบค้นและแก้ไข CSSOM ได้อีกด้วย ตัวอย่างเช่น หากสคริปต์ของคุณขอสไตล์ที่คำนวณแล้วอย่าง element.style.width เบราว์เซอร์จะต้องตรวจสอบให้แน่ใจว่า CSS ทั้งหมดถูกดาวน์โหลดและแยกวิเคราะห์แล้วเพื่อให้คำตอบที่ถูกต้อง สิ่งนี้สร้างการพึ่งพาระหว่าง JavaScript และ CSS ของคุณ ซึ่งการรันสคริปต์อาจถูกบล็อกเพื่อรอให้ CSSOM พร้อมใช้งาน
นอกจากนี้ หาก JavaScript แก้ไข DOM (เช่น เพิ่มหรือลบองค์ประกอบ) หรือ CSSOM (เช่น เปลี่ยนคลาส) มันสามารถกระตุ้นให้เบราว์เซอร์ทำงานเป็นทอดๆ การเปลี่ยนแปลงอาจบังคับให้เบราว์เซอร์ต้องคำนวณ Layout ใหม่ (a reflow) แล้วทำการ re-Paint ส่วนที่ได้รับผลกระทบของหน้าจอใหม่ หรือแม้กระทั่งทั้งหน้าเว็บ การจัดการที่บ่อยครั้งหรือผิดเวลาอาจนำไปสู่อินเทอร์เฟซผู้ใช้ที่ช้าและไม่ตอบสนอง
วิธีวิเคราะห์ Critical Rendering Path
ก่อนที่คุณจะสามารถเพิ่มประสิทธิภาพได้ คุณต้องทำการวัดผลก่อน เครื่องมือสำหรับนักพัฒนาในเบราว์เซอร์ (developer tools) คือเพื่อนที่ดีที่สุดของคุณในการวิเคราะห์ CRP เรามาเน้นที่ Chrome DevTools ซึ่งมีชุดเครื่องมือที่ทรงพลังสำหรับวัตถุประสงค์นี้
การใช้แท็บ Performance
แท็บ Performance ให้ไทม์ไลน์โดยละเอียดของทุกสิ่งที่เบราว์เซอร์ทำเพื่อเรนเดอร์หน้าเว็บของคุณ
- เปิด Chrome DevTools (Ctrl+Shift+I หรือ Cmd+Option+I)
- ไปที่แท็บ Performance
- ตรวจสอบให้แน่ใจว่าได้เลือกช่อง "Web Vitals" เพื่อดูเมตริกสำคัญที่แสดงซ้อนบนไทม์ไลน์
- คลิกปุ่มรีโหลด (หรือกด Ctrl+Shift+E / Cmd+Shift+E) เพื่อเริ่มโปรไฟล์การโหลดหน้าเว็บ
หลังจากที่หน้าเว็บโหลดเสร็จ คุณจะเห็น flame chart นี่คือสิ่งที่ควรมองหาในส่วน Main thread:
- Long Tasks: งานใดๆ ที่ใช้เวลามากกว่า 50 มิลลิวินาทีจะถูกทำเครื่องหมายด้วยสามเหลี่ยมสีแดง นี่คือเป้าหมายหลักสำหรับการเพิ่มประสิทธิภาพเนื่องจากมันบล็อก main thread และอาจทำให้ UI ไม่ตอบสนอง
- Parse HTML (สีน้ำเงิน): ส่วนนี้แสดงให้เห็นว่าเบราว์เซอร์กำลังแยกวิเคราะห์ HTML ของคุณ หากคุณเห็นช่องว่างหรือการหยุดชะงักขนาดใหญ่ อาจเป็นเพราะสคริปต์ที่กำลังบล็อกอยู่
- Evaluate Script (สีเหลือง): นี่คือส่วนที่ JavaScript กำลังถูกรัน มองหาบล็อกสีเหลืองยาวๆ โดยเฉพาะในช่วงแรกของการโหลดหน้าเว็บ นี่คือสคริปต์ที่บล็อกของคุณ
- Recalculate Style (สีม่วง): บ่งชี้ถึงการสร้าง CSSOM และการคำนวณสไตล์
- Layout (สีม่วง): บล็อกเหล่านี้แสดงถึงขั้นตอน Layout หรือ reflow หากคุณเห็นสิ่งเหล่านี้จำนวนมาก JavaScript ของคุณอาจกำลังทำให้เกิด "layout thrashing" โดยการอ่านและเขียนคุณสมบัติทางเรขาคณิตซ้ำๆ
- Paint (สีเขียว): นี่คือกระบวนการวาดภาพ
การใช้แท็บ Network
waterfall chart ในแท็บ Network มีประโยชน์อย่างยิ่งในการทำความเข้าใจลำดับและระยะเวลาในการดาวน์โหลดทรัพยากร
- เปิด DevTools และไปที่แท็บ Network
- รีโหลดหน้าเว็บ
- มุมมอง waterfall จะแสดงให้คุณเห็นว่าแต่ละทรัพยากร (HTML, CSS, JS, รูปภาพ) ถูกร้องขอและดาวน์โหลดเมื่อใด
ให้ความสนใจกับคำขอที่อยู่ด้านบนสุดของ waterfall คุณสามารถมองเห็นไฟล์ CSS และ JavaScript ที่กำลังถูกดาวน์โหลดก่อนที่หน้าเว็บจะเริ่มเรนเดอร์ได้อย่างง่ายดาย สิ่งเหล่านี้คือทรัพยากรที่บล็อกการเรนเดอร์ของคุณ
การใช้ Lighthouse
Lighthouse เป็นเครื่องมือตรวจสอบอัตโนมัติที่ติดตั้งมาใน Chrome DevTools (ใต้แท็บ Lighthouse) มันให้คะแนนประสิทธิภาพในระดับสูงและคำแนะนำที่สามารถนำไปปฏิบัติได้
การตรวจสอบที่สำคัญสำหรับ CRP คือ "Eliminate render-blocking resources." รายงานนี้จะแสดงรายการไฟล์ CSS และ JavaScript ที่ทำให้ First Contentful Paint (FCP) ล่าช้าอย่างชัดเจน ทำให้คุณมีรายการเป้าหมายที่ชัดเจนสำหรับการเพิ่มประสิทธิภาพ
กลยุทธ์หลักในการเพิ่มประสิทธิภาพ JavaScript
ตอนนี้เรารู้วิธีระบุปัญหาแล้ว มาสำรวจวิธีแก้ปัญหากัน เป้าหมายคือการลดปริมาณ JavaScript ที่บล็อกการเรนเดอร์เริ่มต้นให้น้อยที่สุด
1. พลังของ `async` และ `defer`
วิธีที่ง่ายและมีประสิทธิภาพที่สุดในการป้องกันไม่ให้ JavaScript บล็อก HTML parser คือการใช้แอตทริบิวต์ `async` และ `defer` บนแท็ก <script> ของคุณ
<script>แบบมาตรฐาน:<script src="script.js"></script>
ตามที่เราได้กล่าวไปแล้ว นี่คือแบบ parser-blocking การแยกวิเคราะห์ HTML จะหยุดลง, สคริปต์จะถูกดาวน์โหลดและรัน, จากนั้นการแยกวิเคราะห์จะกลับมาทำงานต่อ<script async>:<script src="script.js" async></script>
สคริปต์จะถูกดาวน์โหลดแบบอะซิงโครนัส ควบคู่ไปกับการแยกวิเคราะห์ HTML ทันทีที่สคริปต์ดาวน์โหลดเสร็จสิ้น การแยกวิเคราะห์ HTML จะหยุดชั่วคราวและสคริปต์จะถูกรัน ลำดับการรันไม่สามารถรับประกันได้ สคริปต์จะรันทันทีที่พร้อมใช้งาน วิธีนี้เหมาะที่สุดสำหรับสคริปต์ของบุคคลที่สามที่เป็นอิสระซึ่งไม่ขึ้นอยู่กับ DOM หรือสคริปต์อื่น ๆ เช่น สคริปต์วิเคราะห์ข้อมูลหรือโฆษณา<script defer>:<script src="script.js" defer></script>
สคริปต์จะถูกดาวน์โหลดแบบอะซิงโครนัส ควบคู่ไปกับการแยกวิเคราะห์ HTML อย่างไรก็ตาม สคริปต์จะถูกรันหลังจากที่เอกสาร HTML ถูกแยกวิเคราะห์เสร็จสมบูรณ์แล้วเท่านั้น (ก่อนเหตุการณ์ `DOMContentLoaded`) สคริปต์ที่มี `defer` ยังรับประกันว่าจะรันตามลำดับที่ปรากฏในเอกสาร นี่เป็นวิธีที่แนะนำสำหรับสคริปต์ส่วนใหญ่ที่ต้องโต้ตอบกับ DOM และไม่สำคัญต่อการ paint ครั้งแรก
กฎทั่วไป: ใช้ `defer` สำหรับสคริปต์หลักของแอปพลิเคชันของคุณ ใช้ `async` สำหรับสคริปต์ของบุคคลที่สามที่เป็นอิสระ หลีกเลี่ยงการใช้สคริปต์ที่บล็อกในส่วน <head> เว้นแต่จะจำเป็นอย่างยิ่งสำหรับการเรนเดอร์ครั้งแรก
2. Code Splitting
เว็บแอปพลิเคชันสมัยใหม่มักจะถูกรวม (bundle) เป็นไฟล์ JavaScript ขนาดใหญ่ไฟล์เดียว แม้ว่าวิธีนี้จะช่วยลดจำนวนคำขอ HTTP แต่ก็บังคับให้ผู้ใช้ต้องดาวน์โหลดโค้ดจำนวนมากที่อาจไม่จำเป็นสำหรับมุมมองหน้าเว็บเริ่มต้น
Code Splitting คือกระบวนการแบ่ง bundle ขนาดใหญ่นั้นออกเป็นส่วนเล็กๆ (chunks) ที่สามารถโหลดได้ตามความต้องการ ตัวอย่างเช่น:
- Initial Chunk: ประกอบด้วย JavaScript ที่จำเป็นเท่านั้นในการเรนเดอร์ส่วนที่มองเห็นได้ของหน้าปัจจุบัน
- On-Demand Chunks: ประกอบด้วยโค้ดสำหรับ route อื่นๆ, modals หรือฟีเจอร์ที่อยู่ด้านล่างของหน้าจอ สิ่งเหล่านี้จะถูกโหลดเมื่อผู้ใช้ไปยัง route นั้นหรือโต้ตอบกับฟีเจอร์นั้น
Bundler สมัยใหม่เช่น Webpack, Rollup และ Parcel มีการรองรับ code splitting ในตัวโดยใช้ синтаксис `import()` แบบไดนามิก เฟรมเวิร์กเช่น React (ด้วย `React.lazy`) และ Vue ก็มีวิธีง่ายๆ ในการแบ่งโค้ดในระดับคอมโพเนนต์
3. Tree Shaking และการกำจัดโค้ดที่ไม่ได้ใช้ (Dead Code Elimination)
แม้จะมีการทำ code splitting แต่ bundle เริ่มต้นของคุณอาจมีโค้ดที่ไม่ได้ใช้งานจริง ซึ่งเป็นเรื่องปกติเมื่อคุณนำเข้าไลบรารีแต่ใช้เพียงส่วนเล็กๆ ของมัน
Tree Shaking เป็นกระบวนการที่ใช้โดย bundler สมัยใหม่เพื่อกำจัดโค้ดที่ไม่ได้ใช้ออกจาก bundle สุดท้ายของคุณ มันจะวิเคราะห์คำสั่ง `import` และ `export` ของคุณแบบสแตติกและพิจารณาว่าโค้ดใดที่ไม่สามารถเข้าถึงได้ การทำให้แน่ใจว่าคุณส่งเฉพาะโค้ดที่ผู้ใช้ต้องการ จะช่วยลดขนาด bundle ได้อย่างมาก ซึ่งนำไปสู่การดาวน์โหลดและเวลาในการแยกวิเคราะห์ที่เร็วขึ้น
4. การย่อขนาด (Minification) และการบีบอัด (Compression)
นี่เป็นขั้นตอนพื้นฐานสำหรับเว็บไซต์ที่ใช้งานจริงทุกเว็บ
- Minification: นี่เป็นกระบวนการอัตโนมัติที่ลบอักขระที่ไม่จำเป็นออกจากโค้ดของคุณ เช่น ช่องว่าง, คอมเมนต์ และการขึ้นบรรทัดใหม่ และย่อชื่อตัวแปรให้สั้นลง โดยไม่เปลี่ยนการทำงานของมัน ซึ่งจะช่วยลดขนาดไฟล์ เครื่องมืออย่าง Terser (สำหรับ JavaScript) และ cssnano (สำหรับ CSS) เป็นที่นิยมใช้กันทั่วไป
- Compression: หลังจากการ minification เซิร์ฟเวอร์ของคุณควรบีบอัดไฟล์ก่อนส่งไปยังเบราว์เซอร์ อัลกอริทึมอย่าง Gzip และที่มีประสิทธิภาพมากกว่าอย่าง Brotli สามารถลดขนาดไฟล์ได้ถึง 70-80% จากนั้นเบราว์เซอร์จะขยายไฟล์เมื่อได้รับ นี่คือการกำหนดค่าฝั่งเซิร์ฟเวอร์ แต่มีความสำคัญอย่างยิ่งต่อการลดเวลาในการถ่ายโอนข้อมูลผ่านเครือข่าย
5. การใส่ JavaScript ที่สำคัญแบบอินไลน์ (Use with Caution)
สำหรับ JavaScript ส่วนเล็กๆ ที่จำเป็นอย่างยิ่งสำหรับการ paint ครั้งแรก (เช่น การตั้งค่าธีมหรือ polyfill ที่สำคัญ) คุณสามารถใส่โค้ดเหล่านั้นลงใน HTML ของคุณโดยตรงภายในแท็ก <script> ในส่วน <head> วิธีนี้จะช่วยประหยัดคำขอเครือข่าย ซึ่งอาจเป็นประโยชน์ในการเชื่อมต่อมือถือที่มีความหน่วงสูง อย่างไรก็ตาม ควรใช้วิธีนี้เท่าที่จำเป็น โค้ดที่ใส่แบบอินไลน์จะเพิ่มขนาดของเอกสาร HTML ของคุณและเบราว์เซอร์ไม่สามารถแคชแยกต่างหากได้ มันเป็นการแลกเปลี่ยนที่ควรพิจารณาอย่างรอบคอบ
เทคนิคขั้นสูงและแนวทางสมัยใหม่
Server-Side Rendering (SSR) และ Static Site Generation (SSG)
เฟรมเวิร์กอย่าง Next.js (สำหรับ React), Nuxt.js (สำหรับ Vue) และ SvelteKit ได้ทำให้ SSR และ SSG เป็นที่นิยม เทคนิคเหล่านี้จะย้ายงานเรนเดอร์เริ่มต้นจากเบราว์เซอร์ของไคลเอ็นต์ไปยังเซิร์ฟเวอร์
- SSR: เซิร์ฟเวอร์จะเรนเดอร์ HTML ทั้งหมดสำหรับหน้าที่ร้องขอและส่งไปยังเบราว์เซอร์ เบราว์เซอร์สามารถแสดง HTML นี้ได้ทันที ส่งผลให้ First Contentful Paint รวดเร็วมาก จากนั้น JavaScript จะโหลดและ "hydrates" หน้าเว็บ ทำให้มันสามารถโต้ตอบได้
- SSG: HTML สำหรับทุกหน้าจะถูกสร้างขึ้นในตอน build time เมื่อผู้ใช้ร้องขอหน้าเว็บ ไฟล์ HTML แบบสแตติกจะถูกส่งจาก CDN ทันที นี่เป็นวิธีที่เร็วที่สุดสำหรับเว็บไซต์ที่มีเนื้อหาจำนวนมาก
ทั้ง SSR และ SSG ช่วยปรับปรุงประสิทธิภาพของ CRP อย่างมากโดยการส่งมอบ first paint ที่มีความหมายก่อนที่ JavaScript ฝั่งไคลเอ็นต์ส่วนใหญ่จะเริ่มทำงานเสียอีก
Web Workers
หากแอปพลิเคชันของคุณต้องการทำการคำนวณที่หนักและใช้เวลานาน (เช่น การวิเคราะห์ข้อมูลที่ซับซ้อน, การประมวลผลภาพ หรือการเข้ารหัส) การทำสิ่งนี้บน main thread จะบล็อกการเรนเดอร์และทำให้หน้าเว็บของคุณรู้สึกค้าง Web Workers เป็นทางออกโดยอนุญาตให้คุณรันสคริปต์เหล่านี้ใน background thread ซึ่งแยกออกจาก main UI thread โดยสิ้นเชิง สิ่งนี้ช่วยให้แอปพลิเคชันของคุณตอบสนองได้ดีในขณะที่งานหนักๆ กำลังดำเนินการอยู่เบื้องหลัง
เวิร์กโฟลว์เชิงปฏิบัติสำหรับการเพิ่มประสิทธิภาพ CRP
เรามารวบรวมทั้งหมดเข้าด้วยกันเป็นเวิร์กโฟลว์ที่นำไปปฏิบัติได้ซึ่งคุณสามารถนำไปใช้กับโปรเจกต์ของคุณได้
- ตรวจสอบ (Audit): เริ่มต้นด้วยการวัดค่าพื้นฐาน รันรายงาน Lighthouse และโปรไฟล์ Performance บน production build ของคุณเพื่อทำความเข้าใจสถานะปัจจุบัน จดบันทึกค่า FCP, LCP, TTI ของคุณ และระบุ long tasks หรือทรัพยากรที่บล็อกการเรนเดอร์
- ระบุ (Identify): เจาะลึกเข้าไปในแท็บ Network และ Performance ของ DevTools ระบุให้แน่ชัดว่าสคริปต์และสไตล์ชีตใดที่กำลังบล็อกการเรนเดอร์เริ่มต้น ถามตัวเองสำหรับแต่ละทรัพยากร: "สิ่งนี้จำเป็นอย่างยิ่งสำหรับผู้ใช้ในการเห็นเนื้อหาเริ่มต้นหรือไม่?"
- จัดลำดับความสำคัญ (Prioritize): มุ่งเน้นความพยายามของคุณไปที่โค้ดที่ส่งผลกระทบต่อเนื้อหา above-the-fold เป้าหมายคือการส่งเนื้อหานี้ไปยังผู้ใช้ให้เร็วที่สุดเท่าที่จะทำได้ สิ่งอื่น ๆ สามารถโหลดทีหลังได้
- เพิ่มประสิทธิภาพ (Optimize):
- ใช้
deferกับสคริปต์ที่ไม่จำเป็นทั้งหมด - ใช้
asyncสำหรับสคริปต์ของบุคคลที่สามที่เป็นอิสระ - ใช้ code splitting สำหรับ route และคอมโพเนนต์ขนาดใหญ่ของคุณ
- ตรวจสอบให้แน่ใจว่ากระบวนการ build ของคุณมีการทำ minification และ tree shaking
- ทำงานร่วมกับทีมโครงสร้างพื้นฐานของคุณเพื่อเปิดใช้งานการบีบอัดแบบ Brotli หรือ Gzip บนเซิร์ฟเวอร์ของคุณ
- สำหรับ CSS ให้พิจารณาการใส่ CSS ที่สำคัญที่จำเป็นสำหรับมุมมองเริ่มต้นแบบอินไลน์ และ lazy-loading ส่วนที่เหลือ
- ใช้
- วัดผล (Measure): หลังจากทำการเปลี่ยนแปลงแล้ว ให้รันการตรวจสอบอีกครั้ง เปรียบเทียบคะแนนและเวลาใหม่ของคุณกับค่าพื้นฐาน FCP ของคุณดีขึ้นหรือไม่? มีทรัพยากรที่บล็อกการเรนเดอร์น้อยลงหรือไม่?
- ทำซ้ำ (Iterate): ประสิทธิภาพของเว็บไม่ใช่การแก้ไขครั้งเดียวจบ แต่เป็นกระบวนการต่อเนื่อง เมื่อแอปพลิเคชันของคุณเติบโตขึ้น ปัญหาคอขวดด้านประสิทธิภาพใหม่ๆ ก็อาจเกิดขึ้นได้ ทำให้การตรวจสอบประสิทธิภาพเป็นส่วนหนึ่งของวงจรการพัฒนาและการ deploy ของคุณเป็นประจำ
สรุป: เชี่ยวชาญเส้นทางสู่ประสิทธิภาพ
Critical Rendering Path คือพิมพ์เขียวที่เบราว์เซอร์ใช้เพื่อทำให้แอปพลิเคชันของคุณมีชีวิตขึ้นมา ในฐานะนักพัฒนา ความเข้าใจและการควบคุมเส้นทางนี้ของเรา โดยเฉพาะอย่างยิ่งที่เกี่ยวกับ JavaScript เป็นหนึ่งในเครื่องมือที่ทรงพลังที่สุดที่เรามีเพื่อปรับปรุงประสบการณ์ผู้ใช้ ด้วยการเปลี่ยนกรอบความคิดจากการเขียนโค้ดที่ทำงานได้ ไปสู่การเขียนโค้ดที่มีประสิทธิภาพ เราสามารถสร้างแอปพลิเคชันที่ไม่เพียงแต่ใช้งานได้ แต่ยังรวดเร็ว เข้าถึงได้ และน่าพึงพอใจสำหรับผู้ใช้ทั่วโลก
การเดินทางเริ่มต้นด้วยการวิเคราะห์ เปิดเครื่องมือสำหรับนักพัฒนาของคุณ, โปรไฟล์แอปพลิเคชันของคุณ และเริ่มตั้งคำถามกับทุกทรัพยากรที่ขวางกั้นระหว่างผู้ใช้ของคุณกับหน้าที่เรนเดอร์เสร็จสมบูรณ์ ด้วยการใช้กลยุทธ์ของการเลื่อนเวลาสคริปต์ (deferring), การแบ่งโค้ด (splitting code) และการลดขนาด payload ของคุณ คุณสามารถเคลียร์เส้นทางให้เบราว์เซอร์ทำในสิ่งที่มันทำได้ดีที่สุด: เรนเดอร์เนื้อหาด้วยความเร็วสูง