การแสดงผล HTML ด้วย JavaScript แตกต่างจากการแสดงผล HTML ที่เซิร์ฟเวอร์ส่ง ซึ่งอาจส่งผลต่อประสิทธิภาพ เรียนรู้ความแตกต่างในคู่มือนี้ รวมถึงสิ่งที่คุณสามารถทำได้เพื่อรักษาประสิทธิภาพการแสดงผลของเว็บไซต์ โดยเฉพาะเมื่อเกี่ยวข้องกับการโต้ตอบ
การแยกวิเคราะห์และการแสดงผล HTML เป็นสิ่งที่เบราว์เซอร์ทำได้ดีโดยค่าเริ่มต้นสำหรับเว็บไซต์ที่ใช้ตรรกะการนำทางในตัวของเบราว์เซอร์ ซึ่งบางครั้งเรียกว่า "การโหลดหน้าเว็บแบบดั้งเดิม" หรือ "การนำทางยาก" บางครั้งเราเรียกเว็บไซต์ดังกล่าวว่าแอปพลิเคชันแบบหลายหน้า (MPA)
อย่างไรก็ตาม นักพัฒนาซอฟต์แวร์อาจหลีกเลี่ยงค่าเริ่มต้นของเบราว์เซอร์เพื่อให้เหมาะกับความต้องการของแอปพลิเคชันได้ ในกรณีเช่นนี้สำหรับเว็บไซต์ที่ใช้รูปแบบแอปพลิเคชันหน้าเว็บเดียว (SPA) ซึ่งจะสร้าง HTML/DOM ขนาดใหญ่ในไคลเอ็นต์ด้วย JavaScript แบบไดนามิก การแสดงผลฝั่งไคลเอ็นต์เป็นชื่อสำหรับรูปแบบการออกแบบนี้ และอาจส่งผลกระทบต่อการโต้ตอบกับ Next Paint (INP) ของเว็บไซต์หากงานที่เกี่ยวข้องมีมากเกินไป
คู่มือนี้จะช่วยให้คุณชั่งน้ำหนักความแตกต่างระหว่างการใช้ HTML ที่เซิร์ฟเวอร์ส่งไปยังเบราว์เซอร์กับการสร้างในไคลเอ็นต์ด้วย JavaScript และความแตกต่างระหว่างการใช้ HTML ที่เซิร์ฟเวอร์ส่งมายังเบราว์เซอร์ในช่วงเวลาสำคัญ
วิธีที่เบราว์เซอร์แสดงผล HTML ที่เซิร์ฟเวอร์ให้ไว้
รูปแบบการนำทางที่ใช้ในการโหลดหน้าเว็บแบบดั้งเดิมนั้นเกี่ยวข้องกับการรับ HTML จากเซิร์ฟเวอร์ในการนำทางทุกครั้ง หากคุณป้อน URL ในแถบที่อยู่ของเบราว์เซอร์หรือคลิกลิงก์ใน MPA ชุดเหตุการณ์ต่อไปนี้จะเกิดขึ้น
- เบราว์เซอร์จะส่งคำขอการนำทางสำหรับ URL ที่ระบุ
- เซิร์ฟเวอร์ตอบสนองด้วย HTML เป็นชิ้นส่วน
ขั้นตอนสุดท้ายคือกุญแจสำคัญ นอกจากนี้ยังเป็นการเพิ่มประสิทธิภาพการทำงานขั้นพื้นฐานที่สุดอย่างหนึ่งในการแลกเปลี่ยนเซิร์ฟเวอร์/เบราว์เซอร์ และเป็นที่รู้จักในชื่อสตรีมมิง หากเซิร์ฟเวอร์สามารถเริ่มส่ง HTML โดยเร็วที่สุด และเบราว์เซอร์ไม่รอให้มีการตอบกลับทั้งหมด เบราว์เซอร์จะสามารถประมวลผล HTML เป็นส่วนย่อยที่มาถึงได้
![ภาพหน้าจอของการแยกวิเคราะห์ HTML ที่ส่งโดยเซิร์ฟเวอร์ซึ่งแสดงภาพในแผงประสิทธิภาพของเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome ขณะที่ HTML สตรีมเข้ามา กลุ่มของโค้ดจะถูกประมวลผลในงานที่มีขนาดสั้นลงหลายงานที่สั้นลง และการแสดงภาพจะค่อยๆ เพิ่มขึ้น](https://web.dev/static/articles/client-side-rendering-of-html-and-interactivity/image/a-screenshot-parsing-ht-f2443fe1b1888.png?authuser=5&hl=th)
เช่นเดียวกับเหตุการณ์ส่วนใหญ่ที่เกิดขึ้นในเบราว์เซอร์ การแยกวิเคราะห์ HTML จะเกิดขึ้นภายในงานต่างๆ เมื่อมีการสตรีม HTML จากเซิร์ฟเวอร์ไปยังเบราว์เซอร์ เบราว์เซอร์จะเพิ่มประสิทธิภาพการแยกวิเคราะห์ HTML นั้นโดยดำเนินการทีละเล็กน้อยในขณะที่บิตของสตรีมนั้นส่งเป็นชุดเล็กๆ ผลที่ตามมาก็คือเบราว์เซอร์แสดงผลไปยังเทรดหลักเป็นระยะหลังจากประมวลผลแต่ละกลุ่มเพื่อเลี่ยงงานที่ใช้เวลานาน ซึ่งหมายความว่าอาจมีงานอื่นๆ เกิดขึ้นได้ในขณะที่ระบบกำลังแยกวิเคราะห์ HTML ซึ่งรวมถึงงานการแสดงผลที่เพิ่มขึ้นซึ่งจำเป็นต่อการนำเสนอหน้าเว็บแก่ผู้ใช้ และการประมวลผลการโต้ตอบของผู้ใช้ที่อาจเกิดขึ้นในช่วงเริ่มต้นที่สำคัญของหน้าเว็บ วิธีนี้หมายถึงคะแนน Interaction to Next Paint (INP) ของหน้าเว็บที่ดีขึ้น
สิ่งสำคัญก็คือ เมื่อคุณสตรีม HTML จากเซิร์ฟเวอร์ คุณจะได้รับการแยกวิเคราะห์และแสดงผล HTML เพิ่มขึ้นและแสดงผลชุดข้อความหลักได้ฟรีโดยอัตโนมัติ และจะไม่เข้าใจข้อมูลนั้นหากแสดงผลฝั่งไคลเอ็นต์
วิธีที่เบราว์เซอร์แสดงผล HTML ที่ระบุโดย JavaScript
แม้ว่าคำขอการนำทางไปยังหน้าเว็บหนึ่งๆ จะต้องให้ HTML จำนวนหนึ่งแก่เซิร์ฟเวอร์ แต่บางเว็บไซต์จะใช้รูปแบบ SPA วิธีนี้มักต้องใช้เพย์โหลด HTML เริ่มต้นขั้นต่ำที่เซิร์ฟเวอร์จัดเตรียมไว้ให้ แต่ไคลเอ็นต์จะเติมพื้นที่เนื้อหาหลักของหน้าเว็บที่มี HTML ที่ประกอบขึ้นจากข้อมูลที่ดึงจากเซิร์ฟเวอร์ การนำทางที่ตามมา บางครั้งเรียกว่า "การนำทางเบาๆ" ในกรณีนี้ JavaScript จะเป็นผู้จัดการทั้งหมดเพื่อสร้างหน้าเว็บด้วย HTML ใหม่
การแสดงผลฝั่งไคลเอ็นต์อาจเกิดขึ้นในโฆษณาที่ไม่ใช่แบบ SPA ด้วยในบางกรณีซึ่งมีการเพิ่ม HTML แบบไดนามิกลงใน DOM ผ่าน JavaScript
วิธีสร้าง HTML หรือเพิ่มไปยัง DOM ผ่าน JavaScript มีอยู่สองสามวิธีดังนี้
- พร็อพเพอร์ตี้
innerHTML
ช่วยให้คุณตั้งค่าเนื้อหาในองค์ประกอบที่มีอยู่ผ่านสตริง ซึ่งเบราว์เซอร์จะแยกวิเคราะห์เป็น DOM - เมธอด
document.createElement
ช่วยให้คุณสร้างองค์ประกอบใหม่เพื่อเพิ่มลงใน DOM โดยไม่ต้องใช้การแยกวิเคราะห์ HTML ของเบราว์เซอร์ - ใช้เมธอด
document.write
เพื่อเขียน HTML ลงในเอกสาร (และเบราว์เซอร์จะแยกวิเคราะห์เอกสารนั้น เช่นเดียวกับวิธีการ #1) อย่างไรก็ตาม ด้วยเหตุผลหลายประการ เราไม่แนะนำให้ใช้document.write
อย่างยิ่ง
![ภาพหน้าจอของการแยกวิเคราะห์ HTML ที่แสดงผลผ่าน JavaScript ที่แสดงให้เห็นในแผงประสิทธิภาพของเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome งานจะเกิดขึ้นในงานเดียวที่ใช้เวลานานซึ่งบล็อกเทรดหลัก](https://web.dev/static/articles/client-side-rendering-of-html-and-interactivity/image/a-screenshot-parsing-ht-7b89c1ab03511.png?authuser=5&hl=th)
ผลจากการสร้าง HTML/DOM ผ่าน JavaScript ฝั่งไคลเอ็นต์อาจมีนัยสำคัญดังนี้
- ซึ่งต่างจาก HTML ที่สตรีมโดยเซิร์ฟเวอร์เพื่อตอบสนองคำขอการนำทาง งาน JavaScript ในไคลเอ็นต์จะไม่แบ่งออกเป็นส่วนๆ โดยอัตโนมัติ ซึ่งอาจส่งผลให้งานใช้เวลานานที่บล็อกเทรดหลัก ซึ่งหมายความว่า INP ของหน้าเว็บอาจได้รับผลเสีย หากคุณสร้าง HTML/DOM พร้อมกันในไคลเอ็นต์มากเกินไป
- หากสร้าง HTML บนไคลเอ็นต์ระหว่างการเริ่มต้นใช้งาน ตัวสแกนการโหลดล่วงหน้าของเบราว์เซอร์จะไม่ค้นพบทรัพยากรที่อ้างอิงภายในไคลเอ็นต์ ซึ่งจะส่งผลเสียต่อการแสดงผลเนื้อหาขนาดใหญ่ที่สุด (LCP) ของหน้าเว็บอย่างแน่นอน แม้ว่านี่จะไม่ใช่ปัญหาด้านประสิทธิภาพรันไทม์ (แต่เป็นปัญหาเกี่ยวกับความล่าช้าของเครือข่ายในการดึงข้อมูลทรัพยากรที่สำคัญ) คุณก็ไม่ต้องการให้ LCP ของเว็บไซต์ได้รับผลกระทบจากการหลีกเลี่ยงการเพิ่มประสิทธิภาพพื้นฐานของเบราว์เซอร์แบบพื้นฐานนี้
คุณสามารถทำอะไรได้บ้างเกี่ยวกับผลกระทบด้านประสิทธิภาพของการแสดงผลฝั่งไคลเอ็นต์
หากเว็บไซต์ใช้การแสดงผลฝั่งไคลเอ็นต์อย่างมาก และสังเกตเห็นว่าค่า INP ที่ไม่ดีในข้อมูลภาคสนาม คุณอาจสงสัยว่าการแสดงผลฝั่งไคลเอ็นต์นั้นเกี่ยวข้องกับปัญหานี้หรือไม่ ตัวอย่างเช่น หากเว็บไซต์เป็น SPA ข้อมูลในช่องอาจเผยให้เห็นการโต้ตอบที่ทำให้เกิดการแสดงผลขึ้นอย่างมาก
ไม่ว่าจะเป็นสาเหตุใดก็ตาม ต่อไปนี้เป็นสาเหตุที่เป็นไปได้บางส่วนที่คุณสามารถศึกษาเพื่อช่วยจัดการเรื่องต่างๆ ให้กลับมาเป็นเหมือนเดิมอีกครั้ง
ระบุ HTML จากเซิร์ฟเวอร์ให้ได้มากที่สุด
ดังที่กล่าวไว้ก่อนหน้านี้ เบราว์เซอร์จะจัดการ HTML จากเซิร์ฟเวอร์ด้วยวิธีที่มีประสิทธิภาพมากโดยค่าเริ่มต้น ซึ่งจะช่วยแยกการแยกวิเคราะห์และการแสดงผล HTML ในลักษณะที่หลีกเลี่ยงงานที่ใช้เวลานานและเพิ่มประสิทธิภาพระยะเวลารวมของเทรดหลัก ซึ่งทําให้ Total block Time (TBT) ลดลงและ TBT มีความสัมพันธ์อย่างมากกับ INP
คุณอาจใช้เฟรมเวิร์กฟรอนท์เอนด์ในการสร้างเว็บไซต์ หากใช่ คุณจะต้องตรวจสอบให้แน่ใจว่าคุณกำลังแสดงผล HTML ของคอมโพเนนต์บนเซิร์ฟเวอร์ ซึ่งจะจำกัดจำนวนการแสดงผลเริ่มต้นจากฝั่งไคลเอ็นต์ที่เว็บไซต์ของคุณต้องการ และน่าจะช่วยให้คุณได้รับประสบการณ์การใช้งานที่ดีขึ้น
- สำหรับ React คุณจะต้องใช้ Server DOM API เพื่อแสดง HTML บนเซิร์ฟเวอร์ แต่อย่าลืมว่าวิธีการแสดงผลฝั่งเซิร์ฟเวอร์แบบดั้งเดิมนั้นใช้วิธีการแบบซิงโครนัส ซึ่งอาจใช้เวลานานกว่าข้อมูลเวลาในหน่วยไบต์แรก (TTFB) ที่นานกว่า รวมถึงเมตริกอื่นๆ ที่ตามมา เช่น First Contentful Paint (FCP) และ LCP หากเป็นไปได้ โปรดตรวจสอบว่าคุณใช้ API สตรีมมิงสำหรับ Node.js หรือรันไทม์อื่นๆ ของ JavaScript เพื่อให้เซิร์ฟเวอร์เริ่มสตรีม HTML ไปยังเบราว์เซอร์ได้โดยเร็วที่สุด Next.js เป็นเฟรมเวิร์กที่อิงตาม React มีแนวทางปฏิบัติแนะนำมากมายโดยค่าเริ่มต้น นอกจากการแสดงผล HTML บนเซิร์ฟเวอร์โดยอัตโนมัติแล้ว ยังสามารถสร้าง HTML แบบคงที่สำหรับหน้าเว็บที่ไม่เปลี่ยนแปลงตามบริบทของผู้ใช้ (เช่น การตรวจสอบสิทธิ์) ได้อีกด้วย
- Vue ทำการแสดงผลฝั่งไคลเอ็นต์โดยค่าเริ่มต้นเช่นกัน อย่างไรก็ตาม Vue อย่าง React สามารถแสดงผล HTML ของคอมโพเนนต์บนเซิร์ฟเวอร์ได้เช่นกัน ใช้ประโยชน์จาก API ฝั่งเซิร์ฟเวอร์เหล่านี้เมื่อเป็นไปได้ หรือพิจารณาใช้ Abtraction ระดับสูงสำหรับโปรเจ็กต์ Vue เพื่อช่วยให้นำแนวทางปฏิบัติแนะนำไปใช้ได้ง่ายขึ้น
- Svelte แสดงผล HTML บนเซิร์ฟเวอร์โดยค่าเริ่มต้น แม้ว่าโค้ดคอมโพเนนต์จำเป็นต้องเข้าถึงเนมสเปซเฉพาะของเบราว์เซอร์ (เช่น
window
) คุณอาจแสดงผล HTML ของคอมโพเนนต์นั้นบนเซิร์ฟเวอร์ไม่ได้ ลองสำรวจวิธีการอื่นๆ ทุกครั้งที่ทำได้ เพื่อที่คุณจะได้ไม่ทำให้เกิดการแสดงผลฝั่งไคลเอ็นต์โดยไม่จำเป็น SvelteKit ว่า Svelte จะเป็น Next.js ที่ใช้ React จะฝังแนวทางปฏิบัติแนะนำมากมายลงในโปรเจ็กต์ Svelte ของคุณ เพื่อให้คุณหลีกเลี่ยงข้อผิดพลาดที่อาจเกิดขึ้นในโปรเจ็กต์ที่ใช้ Svelte เพียงอย่างเดียว
จำกัดจำนวนโหนด DOM ที่สร้างขึ้นในไคลเอ็นต์
เมื่อ DOM มีขนาดใหญ่ การประมวลผลที่จำเป็นต่อการแสดงผลมีแนวโน้มที่จะเพิ่มขึ้น ไม่ว่าเว็บไซต์ของคุณจะเป็น SPA แบบเต็ม หรือแทรกโหนดใหม่ลงใน DOM ที่มีอยู่อันเป็นผลมาจากการโต้ตอบสำหรับ MPA ให้พิจารณาทำให้ DOM เหล่านั้นมีขนาดเล็กที่สุดเท่าที่จะเป็นไปได้ ซึ่งจะช่วยลดการทำงานที่จำเป็นระหว่างการแสดงผลฝั่งไคลเอ็นต์เพื่อแสดง HTML นั้น โดยหวังว่าจะช่วยรักษา INP ของเว็บไซต์ของคุณให้ต่ำลง
พิจารณาใช้สถาปัตยกรรมของ Service Worker สตรีมมิง
นี่เป็นเทคนิคขั้นสูง ซึ่งอาจทำได้ยากกับทุกกรณีการใช้งาน แต่ก็เป็นเทคนิคที่สามารถเปลี่ยน MPA ให้เป็นเว็บไซต์ที่ดูเหมือนว่าจะโหลดได้ทันทีเมื่อผู้ใช้เปลี่ยนหน้าจากหน้าหนึ่งไปยังหน้าถัดไป คุณสามารถใช้ Service Worker เพื่อแคชส่วนที่คงที่ของเว็บไซต์ไว้ใน CacheStorage
ล่วงหน้าขณะใช้ ReadableStream
API เพื่อดึง HTML ที่เหลือของหน้าเว็บจากเซิร์ฟเวอร์ได้
เมื่อใช้เทคนิคนี้ได้สำเร็จ แสดงว่าคุณไม่ได้สร้าง HTML บนไคลเอ็นต์ แต่การโหลดส่วนต่างๆ ของเนื้อหาแบบทันทีจากแคชจะทำให้รู้สึกว่าเว็บไซต์ของคุณโหลดได้อย่างรวดเร็ว เว็บไซต์ที่ใช้แนวทางนี้ดูแทบจะเหมือน SPA แต่ไม่มีข้อเสียของการแสดงผลฝั่งไคลเอ็นต์ นอกจากนี้ยังลดจำนวน HTML ที่คุณร้องขอจากเซิร์ฟเวอร์ด้วย
กล่าวโดยสรุปคือ สถาปัตยกรรมโปรแกรมทำงานของบริการสตรีมมิงจะไม่แทนที่ตรรกะการนำทางในตัวของเบราว์เซอร์ แต่จะเพิ่มลงไป โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีดำเนินการดังกล่าวด้วยกล่องงานที่หัวข้อแอปพลิเคชันแบบหลายหน้าที่ทำงานได้เร็วขึ้นด้วยสตรีม
บทสรุป
วิธีที่เว็บไซต์รับและแสดงผล HTML มีผลกระทบต่อประสิทธิภาพ เมื่อคุณพึ่งพาเซิร์ฟเวอร์ในการส่ง HTML ทั้งหมด (หรือทั้งกลุ่ม) ที่จำเป็นสำหรับเว็บไซต์ของคุณในการทำงาน คุณจะได้รับข้อมูลฟรีมากมาย ไม่ว่าจะเป็นการแยกวิเคราะห์และการแสดงผลที่เพิ่มขึ้น และการใช้ชุดข้อความหลักโดยอัตโนมัติเพื่อหลีกเลี่ยงงานที่ใช้เวลานาน
การแสดงผล HTML ฝั่งไคลเอ็นต์จะทำให้เกิดปัญหาด้านประสิทธิภาพที่อาจเกิดขึ้นมากมาย ซึ่งคุณสามารถหลีกเลี่ยงได้ในหลายๆ กรณี อย่างไรก็ตาม ด้วยข้อกำหนดของแต่ละเว็บไซต์ จึงไม่สามารถหลีกเลี่ยงได้ 100% ตลอดเวลา เพื่อลดงานที่ใช้เวลานานซึ่งอาจเกิดจากการแสดงผลเว็บไซต์ไคลเอ็นต์มากเกินไป คุณต้องส่ง HTML ของเว็บไซต์จากเซิร์ฟเวอร์ให้มากที่สุดเท่าที่จะเป็นไปได้ และพยายามทำให้ DOM มีขนาดเล็กลงที่สุดสำหรับ HTML ที่ต้องแสดงผลบนไคลเอ็นต์ และพิจารณาสถาปัตยกรรมสำรองเพื่อเร่งการส่ง HTML ไปยังไคลเอ็นต์ ในขณะเดียวกันก็ใช้ประโยชน์จากการแยกวิเคราะห์และการแสดงผลที่เพิ่มขึ้นของเบราว์เซอร์ HTML ที่โหลดจากเซิร์ฟเวอร์
หากคุณสามารถทำให้การแสดงผลฝั่งไคลเอ็นต์ของเว็บไซต์น้อยที่สุดเท่าที่จะเป็นไปได้ได้ คุณจะไม่เพียงแต่ปรับปรุง INP ของเว็บไซต์เท่านั้น แต่ยังเป็นเมตริกอื่นๆ เช่น LCP, TBT และอาจรวมถึง TTFB ในบางกรณี
รูปภาพหลักจาก Unsplash โดย Maik Jonietz