การแสดงผล HTML ด้วย JavaScript แตกต่างจากการแสดงผล HTML ที่เซิร์ฟเวอร์ส่งมา ซึ่งอาจส่งผลต่อประสิทธิภาพ ดูความแตกต่างในคู่มือนี้ และสิ่งที่คุณทำได้เพื่อรักษาประสิทธิภาพการแสดงผลของเว็บไซต์ โดยเฉพาะอย่างยิ่งในส่วนที่เกี่ยวข้องกับการโต้ตอบ
การแยกวิเคราะห์และการแสดงผล HTML เป็นสิ่งที่เบราว์เซอร์ทำได้ดีมากโดยค่าเริ่มต้นสำหรับเว็บไซต์ที่ใช้ตรรกะการนำทางในตัวของเบราว์เซอร์ ซึ่งบางครั้งเรียกว่า "การโหลดหน้าเว็บแบบเดิม" หรือ "การนำทางแบบฮาร์ด" บางครั้งเราเรียกเว็บไซต์ดังกล่าวว่าแอปพลิเคชันแบบหลายหน้า (MPA)
อย่างไรก็ตาม นักพัฒนาแอปอาจหลีกเลี่ยงค่าเริ่มต้นของเบราว์เซอร์เพื่อให้เหมาะกับความต้องการของแอปพลิเคชัน แน่นอนว่านี่เป็นกรณีสำหรับเว็บไซต์ที่ใช้รูปแบบแอปพลิเคชันหน้าเว็บเดียว (SPA) ซึ่งสร้างส่วนใหญ่ของ HTML/DOM แบบไดนามิกในไคลเอ็นต์ด้วย JavaScript การแสดงผลฝั่งไคลเอ็นต์เป็นชื่อของรูปแบบการออกแบบนี้ และอาจส่งผลต่อ Interaction to Next Paint (INP) ของเว็บไซต์หากมีงานที่เกี่ยวข้องมากเกินไป
คู่มือนี้จะช่วยให้คุณพิจารณาความแตกต่างระหว่างการใช้ HTML ที่เซิร์ฟเวอร์ส่งไปยังเบราว์เซอร์กับการสร้าง HTML ในไคลเอ็นต์ด้วย JavaScript และวิธีที่การสร้าง HTML ในไคลเอ็นต์อาจส่งผลให้เกิดเวลาในการตอบสนองต่อการโต้ตอบสูงในช่วงเวลาสําคัญ
วิธีที่เบราว์เซอร์แสดงผล HTML ที่เซิร์ฟเวอร์ระบุ
รูปแบบการนำทางที่ใช้ในการโหลดหน้าเว็บแบบเดิมเกี่ยวข้องกับการรับ HTML จากเซิร์ฟเวอร์ในการนำทางทุกครั้ง หากคุณป้อน URL ในแถบที่อยู่ของเบราว์เซอร์หรือคลิกลิงก์ใน MPA จะเกิดเหตุการณ์ต่อไปนี้
- เบราว์เซอร์จะส่งคำขอการนำทางสำหรับ URL ที่ระบุ
- เซิร์ฟเวอร์จะตอบกลับด้วย HTML ในรูปแบบก้อน
ขั้นตอนสุดท้ายของกระบวนการเหล่านี้เป็นสิ่งสำคัญ นอกจากนี้ยังเป็นการเพิ่มประสิทธิภาพที่สำคัญที่สุดอย่างหนึ่งในการแลกเปลี่ยนเซิร์ฟเวอร์/เบราว์เซอร์ และเป็นที่รู้จักกันในชื่อการสตรีม หากเซิร์ฟเวอร์เริ่มส่ง HTML ได้โดยเร็วที่สุด และเบราว์เซอร์ไม่ต้องรอให้การตอบกลับทั้งหมดมาถึง เบราว์เซอร์จะประมวลผล HTML เป็นกลุ่มได้เมื่อมาถึง

การแยกวิเคราะห์ 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 มีวิธีที่พบบ่อย 2-3 วิธีดังนี้
- พร็อพเพอร์ตี้
innerHTML
ช่วยให้คุณตั้งค่าเนื้อหาในองค์ประกอบที่มีอยู่ผ่านสตริง ซึ่งเบราว์เซอร์จะแยกวิเคราะห์เป็น DOM document.createElement
วิธีช่วยให้คุณสร้างองค์ประกอบใหม่เพื่อเพิ่มลงใน DOM ได้โดยไม่ต้องใช้การแยกวิเคราะห์ HTML ของเบราว์เซอร์document.write
วิธีนี้ช่วยให้คุณเขียน HTML ลงในเอกสารได้ (และเบราว์เซอร์จะแยกวิเคราะห์เช่นเดียวกับในวิธีที่ 1) อย่างไรก็ตาม ด้วยเหตุผลหลายประการ เราไม่แนะนำให้ใช้document.write

ผลที่ตามมาของการสร้าง HTML/DOM ผ่าน JavaScript ฝั่งไคลเอ็นต์อาจมีนัยสำคัญ ดังนี้
- งาน JavaScript ในไคลเอ็นต์จะไม่ถูกแบ่งออกเป็นกลุ่มโดยอัตโนมัติ ซึ่งอาจส่งผลให้เกิดงานที่ใช้เวลานานซึ่งบล็อกเทรดหลัก ซึ่งแตกต่างจาก HTML ที่เซิร์ฟเวอร์สตรีมในการตอบสนองต่อคำขอไปยังส่วนต่างๆ ซึ่งหมายความว่า INP ของหน้าเว็บอาจได้รับผลกระทบในทางลบหากคุณสร้าง HTML/DOM มากเกินไปในครั้งเดียวบนไคลเอ็นต์
- หากสร้าง HTML ในไคลเอ็นต์ระหว่างการเริ่มต้นทำงาน เครื่องสแกนการโหลดล่วงหน้าของเบราว์เซอร์จะไม่ค้นพบทรัพยากรที่อ้างอิงภายใน ซึ่งจะส่งผลเสียต่อ Largest Contentful Paint (LCP) ของหน้าเว็บอย่างแน่นอน แม้ว่านี่จะไม่ใช่ปัญหาด้านประสิทธิภาพขณะรันไทม์ (แต่เป็นปัญหาความล่าช้าของเครือข่ายในการดึงข้อมูลทรัพยากรที่สำคัญ) คุณก็ไม่ต้องการให้ LCP ของเว็บไซต์ได้รับผลกระทบจากการหลีกเลี่ยงการเพิ่มประสิทธิภาพพื้นฐานของเบราว์เซอร์นี้
สิ่งที่คุณทำได้เกี่ยวกับผลกระทบต่อประสิทธิภาพของการแสดงผลฝั่งไคลเอ็นต์
หากเว็บไซต์ของคุณขึ้นอยู่กับการแสดงผลฝั่งไคลเอ็นต์เป็นอย่างมาก และคุณสังเกตเห็นค่า INP ที่ไม่ดีในข้อมูลภาคสนาม คุณอาจสงสัยว่าการแสดงผลฝั่งไคลเอ็นต์เกี่ยวข้องกับปัญหาหรือไม่ ตัวอย่างเช่น หากเว็บไซต์เป็น SPA ข้อมูลภาคสนามอาจแสดงให้เห็นการโต้ตอบที่รับผิดชอบงานการแสดงผลจำนวนมาก
ไม่ว่าสาเหตุจะเป็นอะไรก็ตาม สาเหตุที่เป็นไปได้บางอย่างที่คุณสามารถตรวจสอบเพื่อช่วยให้ทุกอย่างกลับมาเป็นปกติมีดังนี้
ระบุ HTML จากเซิร์ฟเวอร์ให้ได้มากที่สุด
ดังที่ได้กล่าวไว้ก่อนหน้านี้ เบราว์เซอร์จะจัดการ HTML จากเซิร์ฟเวอร์ในลักษณะที่มีประสิทธิภาพมากโดยค่าเริ่มต้น ซึ่งจะแบ่งการแยกวิเคราะห์และการแสดงผล HTML ในลักษณะที่หลีกเลี่ยงงานที่ใช้เวลานาน และเพิ่มประสิทธิภาพระยะเวลาของเทรดหลักทั้งหมด ซึ่งจะส่งผลให้เวลาในการบล็อกทั้งหมด (TBT) ลดลง และ TBT มีความสัมพันธ์อย่างมากกับ INP
คุณอาจใช้เฟรมเวิร์กส่วนหน้าเพื่อสร้างเว็บไซต์ ในกรณีนี้ คุณจะต้องตรวจสอบว่าได้แสดงผล HTML ของคอมโพเนนต์ในเซิร์ฟเวอร์ ซึ่งจะจำกัดจำนวนการแสดงผลฝั่งไคลเอ็นต์เริ่มต้นที่เว็บไซต์ของคุณต้องใช้ และควรส่งผลให้ได้รับประสบการณ์ที่ดีขึ้น
- สำหรับ React คุณจะต้องใช้ Server DOM API เพื่อแสดงผล HTML ในเซิร์ฟเวอร์ แต่โปรดทราบว่าวิธีการแสดงผลฝั่งเซิร์ฟเวอร์แบบเดิมใช้วิธีการแบบซิงโครนัส ซึ่งอาจทำให้Time to First Byte (TTFB) นานขึ้น รวมถึงเมตริกอื่นๆ ที่ตามมา เช่น First Contentful Paint (FCP) และ LCP หากเป็นไปได้ โปรดตรวจสอบว่าคุณใช้ Streaming API สำหรับ Node.js หรือรันไทม์ JavaScript อื่นๆ เพื่อให้เซิร์ฟเวอร์เริ่มสตรีม HTML ไปยังเบราว์เซอร์ได้โดยเร็วที่สุด Next.js ซึ่งเป็นเฟรมเวิร์กที่ใช้ React มีแนวทางปฏิบัติแนะนำมากมายโดยค่าเริ่มต้น นอกจากจะแสดงผล HTML บนเซิร์ฟเวอร์โดยอัตโนมัติแล้ว ยังสร้าง HTML แบบคงที่สำหรับหน้าเว็บที่ไม่เปลี่ยนแปลงตามบริบทของผู้ใช้ (เช่น การตรวจสอบสิทธิ์) ได้อีกด้วย
- นอกจากนี้ Vue ยังทำการแสดงผลฝั่งไคลเอ็นต์โดยค่าเริ่มต้นด้วย อย่างไรก็ตาม Vue ก็สามารถแสดงผล HTML ของคอมโพเนนต์ในเซิร์ฟเวอร์ได้เช่นเดียวกับ React ใช้ประโยชน์จาก API ฝั่งเซิร์ฟเวอร์เหล่านี้หากเป็นไปได้ หรือพิจารณาการแยกข้อมูลระดับสูงสำหรับโปรเจ็กต์ 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 ที่คุณขอจากเซิร์ฟเวอร์ด้วย
กล่าวโดยย่อคือ สถาปัตยกรรมของ Streaming Service Worker ไม่ได้แทนที่ตรรกะการนำทางในตัวของเบราว์เซอร์ แต่เพิ่มลงไป ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีดำเนินการนี้ด้วย Workbox ได้ที่แอปพลิเคชันแบบหลายหน้าเว็บที่เร็วขึ้นด้วยสตรีม
บทสรุป
วิธีที่เว็บไซต์รับและแสดงผล HTML จะส่งผลต่อประสิทธิภาพ เมื่อคุณใช้เซิร์ฟเวอร์เพื่อส่ง HTML ทั้งหมด (หรือส่วนใหญ่) ที่จำเป็นต่อการทำงานของเว็บไซต์ คุณจะได้รับประโยชน์มากมายโดยไม่ต้องเสียค่าใช้จ่าย ได้แก่ การแยกวิเคราะห์และการแสดงผลแบบเพิ่มทีละส่วน รวมถึงการส่งต่อการทำงานไปยังเทรดหลักโดยอัตโนมัติเพื่อหลีกเลี่ยงงานที่ใช้เวลานาน
การแสดงผล HTML ฝั่งไคลเอ็นต์ทำให้เกิดปัญหาด้านประสิทธิภาพที่อาจเกิดขึ้นได้หลายประการ ซึ่งในหลายกรณีก็สามารถหลีกเลี่ยงได้ อย่างไรก็ตาม เนื่องจากข้อกำหนดของแต่ละเว็บไซต์ จึงไม่สามารถหลีกเลี่ยงได้ 100% หากต้องการลดภาระงานที่อาจใช้เวลานานซึ่งเกิดจากการแสดงผลฝั่งไคลเอ็นต์มากเกินไป ให้ตรวจสอบว่าคุณส่ง HTML ของเว็บไซต์จากเซิร์ฟเวอร์มากที่สุดเท่าที่จะเป็นไปได้ทุกครั้ง พยายามให้ขนาด DOM เล็กที่สุดเท่าที่จะเป็นไปได้สำหรับ HTML ที่ต้องแสดงผลในไคลเอ็นต์ และพิจารณาสถาปัตยกรรมทางเลือกเพื่อเพิ่มความเร็วในการส่ง HTML ไปยังไคลเอ็นต์ พร้อมทั้งใช้ประโยชน์จากการแยกวิเคราะห์และการแสดงผลแบบเพิ่มทีละส่วนที่เบราว์เซอร์มีให้สำหรับ HTML ที่โหลดจากเซิร์ฟเวอร์
หากลดการแสดงผลฝั่งไคลเอ็นต์ของเว็บไซต์ให้เหลือน้อยที่สุดได้ คุณจะไม่เพียงปรับปรุง INP ของเว็บไซต์เท่านั้น แต่ยังปรับปรุงเมตริกอื่นๆ เช่น LCP, TBT และอาจรวมถึง TTFB ในบางกรณีด้วย
รูปภาพหลักจาก Unsplash โดย Maik Jonietz