มาเรียนรู้กันว่าเครื่องมือสแกนการโหลดล่วงหน้าของเบราว์เซอร์คืออะไร จะช่วยเพิ่มประสิทธิภาพได้อย่างไร และวิธีป้องกันปัญหาในการใช้งาน
แง่มุมหนึ่งที่มองข้ามไปเกี่ยวกับการเพิ่มประสิทธิภาพความเร็วหน้าเว็บคือการทำความเข้าใจเกี่ยวกับเบราว์เซอร์ภายใน เบราว์เซอร์จะทำการเพิ่มประสิทธิภาพบางอย่างเพื่อปรับปรุงประสิทธิภาพในแบบที่เราในฐานะนักพัฒนาซอฟต์แวร์ทำไม่ได้ แต่ตราบใดที่การเพิ่มประสิทธิภาพเหล่านั้นไม่ถูกขัดขวางโดยไม่ตั้งใจ
การเพิ่มประสิทธิภาพเบราว์เซอร์ภายในอย่างหนึ่งที่ต้องทำความเข้าใจคือตัวสแกนการโหลดล่วงหน้าของเบราว์เซอร์ โพสต์นี้จะอธิบายวิธีการทำงานของเครื่องสแกนการโหลดล่วงหน้า และที่สำคัญกว่านั้นคือวิธีหลีกเลี่ยงการขัดขวางการทำงาน
เครื่องสแกนการโหลดล่วงหน้าคืออะไร
ทุกเบราว์เซอร์จะมีโปรแกรมแยกวิเคราะห์ HTML หลักที่แปลงมาร์กอัปดิบและประมวลผลเป็นโมเดลออบเจ็กต์ ปัญหานี้จะดำเนินต่อไปจนกว่าโปรแกรมแยกวิเคราะห์จะหยุดชั่วคราวเมื่อพบทรัพยากรที่บล็อก เช่น สไตล์ชีตที่โหลดด้วยองค์ประกอบ <link>
หรือสคริปต์ที่โหลดด้วยองค์ประกอบ <script>
โดยไม่มีแอตทริบิวต์ async
หรือ defer
<link>
สำหรับไฟล์ CSS ภายนอก ซึ่งจะบล็อกเบราว์เซอร์ไม่ให้แยกวิเคราะห์ส่วนที่เหลือของเอกสาร หรือแม้กระทั่งแสดงผลเอกสารใดๆ จนกว่าจะมีการดาวน์โหลดและแยกวิเคราะห์ CSS
ในกรณีของไฟล์ CSS ทั้งการแยกวิเคราะห์และการแสดงผลจะถูกบล็อกเพื่อป้องกันไม่ให้มีการแสดง Flash ของเนื้อหาที่ไม่มีการจัดรูปแบบ (FOUC) ซึ่งก็คือการเห็นหน้าเว็บเวอร์ชันที่ไม่มีการจัดรูปแบบเป็นระยะเวลาสั้นๆ ก่อนที่จะนำรูปแบบไปใช้
![หน้าแรกของ web.dev ในสถานะที่ไม่มีการจัดรูปแบบ (ซ้าย) และมีสถานะจัดรูปแบบ (ขวา)](https://web.dev/static/articles/preload-scanner/image/the-webdev-home-page-an-d03ddaf5d79f7.png?authuser=0&hl=th)
เบราว์เซอร์ยังบล็อกการแยกวิเคราะห์และการแสดงผลหน้าเว็บเมื่อเจอองค์ประกอบ <script>
ที่ไม่มีแอตทริบิวต์ defer
หรือ async
อีกด้วย
สาเหตุก็คือเบราว์เซอร์ไม่สามารถรู้ได้อย่างแน่ชัดว่าสคริปต์ใดๆ ที่กำหนดจะแก้ไข DOM หรือไม่ ในขณะที่โปรแกรมแยกวิเคราะห์ HTML หลักยังคงทํางานอยู่ ด้วยเหตุนี้ การที่เว็บไซต์โหลด JavaScript ต่อท้ายเอกสารจึงถือเป็นเรื่องปกติ เพื่อให้ผลของการแยกวิเคราะห์และการแสดงผลที่บล็อกได้กลายเป็นส่วนเพิ่ม
สาเหตุเหล่านี้คือเหตุผลที่เบราว์เซอร์ควรบล็อกทั้งการแยกวิเคราะห์และการแสดงผล อย่างไรก็ตาม การบล็อกขั้นตอนสำคัญขั้นตอนใดขั้นตอนหนึ่งนั้นไม่เป็นที่ต้องการ เนื่องจากอาจทำให้การแสดงล่าช้ากว่าการค้นพบแหล่งข้อมูลสำคัญอื่นๆ โชคดีที่เบราว์เซอร์พยายามอย่างสุดความสามารถเพื่อลดปัญหาเหล่านี้โดยใช้โปรแกรมแยกวิเคราะห์ HTML รองที่เรียกว่าโปรแกรมสแกนล่วงหน้า
<body>
แต่เครื่องสแกนการโหลดล่วงหน้าอาจดูในมาร์กอัปดิบเพื่อหาทรัพยากรรูปภาพและเริ่มโหลดก่อนที่โปรแกรมแยกวิเคราะห์ HTML หลักจะถูกบล็อก
บทบาทของเครื่องสแกนการโหลดล่วงหน้านั้นเป็นแบบคาดเดา ซึ่งหมายความว่าโปรแกรมจะตรวจสอบมาร์กอัปดิบเพื่อค้นหาทรัพยากรที่จะดึงข้อมูลตามโอกาสก่อนที่โปรแกรมแยกวิเคราะห์ HTML หลักจะค้นพบทรัพยากรเหล่านั้น
วิธีดูว่าเครื่องสแกนการโหลดล่วงหน้าทำงานอยู่เมื่อใด
ตัวสแกนการโหลดล่วงหน้ามีอยู่เนื่องจากการแสดงผลและการแยกวิเคราะห์ถูกบล็อก หากไม่เคยมีปัญหาด้านประสิทธิภาพทั้งสองนี้ ตัวสแกนการโหลดล่วงหน้าก็จะไม่มีประโยชน์มากนัก กุญแจสำคัญในการตรวจสอบว่าหน้าเว็บได้รับประโยชน์จากตัวสแกนการโหลดล่วงหน้าหรือไม่ จะขึ้นอยู่กับปรากฏการณ์การปิดกั้นเหล่านี้ ซึ่งทำได้โดยกำหนดการหน่วงเวลาสมมติให้คำขอทราบว่าเครื่องสแกนการโหลดล่วงหน้าทำงานอยู่ที่ตำแหน่งใด
ดูหน้านี้สำหรับข้อความและรูปภาพพื้นฐานด้วยสไตล์ชีตเป็นตัวอย่าง เนื่องจากไฟล์ CSS จะบล็อกทั้งการแสดงผลและการแยกวิเคราะห์ คุณจึงทำให้สไตล์ชีตล่าช้าไป 2 วินาทีผ่านบริการพร็อกซี การหน่วงเวลานี้ช่วยให้เห็น Waterfall ของเครือข่าย ซึ่งเป็นจุดที่เครื่องสแกนการโหลดล่วงหน้าทำงานอยู่ได้ง่ายขึ้น
![แผนภูมิ Waterfall ของเครือข่าย WebPageTest แสดงการหน่วงเวลาสมมติ 2 วินาทีที่กำหนดในสไตล์ชีต](https://web.dev/static/articles/preload-scanner/image/the-webpagetest-network-w-888b82cd76bf1.png?authuser=0&hl=th)
อย่างที่เห็นใน Waterfall เครื่องสแกนการโหลดล่วงหน้าจะค้นพบองค์ประกอบ <img>
แม้ว่าการแสดงผลและการแยกวิเคราะห์เอกสารจะถูกบล็อก หากไม่ใช้การเพิ่มประสิทธิภาพนี้ เบราว์เซอร์จะไม่สามารถดึงข้อมูลได้เมื่อเป็นไปตามโอกาสในระหว่างช่วงเวลาการบล็อก และการส่งคำขอทรัพยากรเพิ่มเติมจะเป็นแบบต่อเนื่องกันมากกว่าเกิดขึ้นพร้อมกัน
จากตัวอย่างของเล่นที่ยังหลงเหลืออยู่แล้ว เรามาดูตัวอย่างรูปแบบในชีวิตจริงที่อาจแย่งตัวสแกนการโหลดล่วงหน้าได้ และมีอะไรที่ควรแก้ไขได้บ้าง
แทรกสคริปต์แล้ว async
รายการ
สมมติว่าคุณมี HTML ใน <head>
ที่มี JavaScript ในหน้าดังนี้
<script>
const scriptEl = document.createElement('script');
scriptEl.src = '/yall.min.js';
document.head.appendChild(scriptEl);
</script>
สคริปต์ที่แทรกจะเป็น async
โดยค่าเริ่มต้น ดังนั้นเมื่อมีการแทรกสคริปต์นี้ สคริปต์จะทำงานเสมือนว่ามีแอตทริบิวต์ async
เข้ามาด้วย ซึ่งหมายความว่าโฆษณาจะทำงานโดยเร็วที่สุดและไม่บล็อกการแสดงผล ฟังดูเหมาะสมที่สุดใช่ไหม แต่หากคุณสันนิษฐานว่า <script>
ในบรรทัดนี้อยู่หลังองค์ประกอบ <link>
ที่โหลดไฟล์ CSS ภายนอก คุณจะได้รับผลลัพธ์ที่มีประสิทธิภาพต่ำกว่าเกณฑ์ ดังนี้
![แผนภูมิ WebPageTest นี้แสดงการสแกนการโหลดล่วงหน้าที่เสียไปเมื่อแทรกสคริปต์](https://web.dev/static/articles/preload-scanner/image/this-webpagetest-chart-sh-d83c16fca193.png?authuser=0&hl=th)
async
ที่แทรกไว้ ตัวสแกนการโหลดล่วงหน้าไม่พบสคริปต์ในระหว่างขั้นตอนการบล็อกการแสดงผล เนื่องจากมีการแทรกสคริปต์ในไคลเอ็นต์
มาดูรายละเอียดสิ่งที่เกิดขึ้นที่นี่
- เมื่อเวลา 0 วินาที จะมีการขอเอกสารหลัก
- เมื่อเวลา 1.4 วินาที ไบต์แรกของคำขอการนำทางจะมาถึง
- และที่เวลา 2.0 วินาที จะมีการขอ CSS และรูปภาพ
- เนื่องจากโปรแกรมแยกวิเคราะห์ถูกบล็อกไม่ให้โหลดสไตล์ชีต และ JavaScript แบบอินไลน์ที่แทรกสคริปต์
async
จะเข้ามาหลังจากสไตล์ชีตนั้นที่เวลา 2.6 วินาที ฟังก์ชันที่สคริปต์ให้จึงไม่สามารถใช้งานได้ในทันที
ซึ่งเป็นค่าที่ต่ำกว่าประสิทธิภาพ เนื่องจากคำขอสำหรับสคริปต์จะเกิดขึ้นหลังจากที่ดาวน์โหลดสไตล์ชีตเสร็จแล้วเท่านั้น การดำเนินการนี้จะทำให้สคริปต์ไม่ทำงานโดยเร็วที่สุด ในทางตรงกันข้าม เนื่องจากองค์ประกอบ <img>
ค้นพบได้ในมาร์กอัปที่เซิร์ฟเวอร์มีให้ ตัวสแกนการโหลดล่วงหน้าจึงค้นพบได้
จะเกิดอะไรขึ้นหากคุณใช้แท็ก <script>
ปกติที่มีแอตทริบิวต์ async
มากกว่าการแทรกสคริปต์ลงใน DOM
<script src="/yall.min.js" async></script>
นี่คือผลลัพธ์:
![Waterfall เครือข่าย WebPageTest ซึ่งแสดงวิธีที่โปรแกรมสแกนการโหลดล่วงหน้าของเบราว์เซอร์ยังค้นพบสคริปต์ที่ไม่พร้อมกันที่โหลดโดยใช้องค์ประกอบสคริปต์ HTML ได้ แม้ว่าโปรแกรมแยกวิเคราะห์ HTML หลักของเบราว์เซอร์จะถูกบล็อกขณะดาวน์โหลดและประมวลผลสไตล์ชีตก็ตาม](https://web.dev/static/articles/preload-scanner/image/a-webpagetest-network-wat-02055245d5ef.png?authuser=0&hl=th)
async
<script>
รายการเดียว ตัวสแกนการโหลดล่วงหน้าจะค้นหาสคริปต์ในระหว่างขั้นตอนการบล็อกการแสดงผล และโหลดไปพร้อมกับ CSS
เราอยากแนะนำให้ผู้ใช้ rel=preload
สามารถแก้ไขปัญหาเหล่านี้ได้ วิธีนี้ได้ผลแน่นอน แต่อาจมีผลข้างเคียงบางอย่างด้วย จริงๆ แล้วทำไมต้องใช้ rel=preload
เพื่อแก้ปัญหาที่ไม่แทรกด้วยองค์ประกอบ <script>
ใน DOM ล่ะ
![Waterfall ของ WebPageTest จะแสดงวิธีใช้คำแนะนำทรัพยากร rel=preload เพื่อโปรโมตการค้นพบสคริปต์ที่แทรกแบบไม่พร้อมกัน แม้ว่าจะก่อให้เกิดผลข้างเคียงโดยไม่ตั้งใจก็ตาม](https://web.dev/static/articles/preload-scanner/image/a-webpagetest-waterfall-s-2f698a04955f.png?authuser=0&hl=th)
async
ที่แทรกเข้ามา แต่ระบบจะโหลดสคริปต์ async
ล่วงหน้าเพื่อให้ค้นพบได้เร็วขึ้น
การโหลดล่วงหน้าจะ "แก้ไข" ปัญหาที่นี่ แต่ทำให้เกิดปัญหาใหม่ นั่นคือ สคริปต์ async
ในการสาธิต 2 รายการแรก (แม้ว่าจะโหลดใน <head>
) ด้วยลำดับความสำคัญ "ต่ำ" ขณะที่สไตล์ชีตโหลดโดยใช้ลำดับความสำคัญ "สูงสุด" ในการสาธิตล่าสุดที่มีการโหลดสคริปต์ async
ล่วงหน้า สไตล์ชีตจะยังคงโหลดที่ลำดับความสำคัญ "สูงสุด" แต่ลำดับความสำคัญของสคริปต์ได้รับการโปรโมตเป็น "สูง"
เมื่อมีการเพิ่มลำดับความสำคัญของทรัพยากร เบราว์เซอร์จะจัดสรรแบนด์วิดท์ให้ทรัพยากรนั้นมากขึ้น ซึ่งหมายความว่าแม้ว่าสไตล์ชีตจะมีลำดับความสำคัญสูงสุด แต่ลำดับความสำคัญที่เพิ่มขึ้นของสคริปต์อาจทำให้เกิดการช่วงชิงแบนด์วิดท์ได้ ซึ่งอาจเป็นปัจจัยที่ทำให้การเชื่อมต่อช้า หรือในกรณีที่ทรัพยากรค่อนข้างใหญ่
คำตอบนี้ไม่ยากเลย หากจำเป็นต้องใช้สคริปต์ในระหว่างการเริ่มต้น อย่าทำลายเครื่องสแกนการโหลดล่วงหน้าโดยการแทรกสคริปต์ลงใน DOM ทำการทดสอบตามต้องการกับตำแหน่งองค์ประกอบ <script>
รวมถึงแอตทริบิวต์ เช่น defer
และ async
การโหลดแบบ Lazy Loading ด้วย JavaScript
การโหลดแบบ Lazy Loading เป็นวิธีการที่ยอดเยี่ยมในการประหยัดข้อมูลซึ่งมักใช้กับรูปภาพ อย่างไรก็ตาม บางครั้งการโหลดแบบ Lazy Loading อาจใช้ไม่ได้กับรูปภาพที่อยู่ "ครึ่งหน้าบน"
ซึ่งก่อให้เกิดปัญหาที่อาจเกิดขึ้นกับการค้นพบทรัพยากรซึ่งเกี่ยวข้องกับตัวสแกนการโหลดล่วงหน้า และอาจเลื่อนเวลาในการค้นหาการอ้างอิงไปยังรูปภาพ ดาวน์โหลด ถอดรหัส และนำเสนอรูปภาพนั้นโดยไม่จำเป็น ลองดูมาร์กอัปรูปภาพนี้เป็นตัวอย่าง
<img data-src="/sand-wasp.jpg" alt="Sand Wasp" width="384" height="255">
การใช้คำนำหน้า data-
เป็นรูปแบบที่พบได้ทั่วไปใน Lazy Loading ที่ทำงานด้วย JavaScript เมื่อเลื่อนรูปภาพเข้าสู่วิวพอร์ต ตัวโหลดแบบ Lazy Loading จะตัดคำนำหน้า data-
ออก ซึ่งหมายความว่าในตัวอย่างก่อนหน้านี้ data-src
จะกลายเป็น src
โดยการอัปเดตนี้จะแจ้งให้เบราว์เซอร์ดึงข้อมูลทรัพยากร
รูปแบบนี้ไม่เป็นปัญหาจนกว่าจะนำไปใช้กับรูปภาพที่อยู่ในวิวพอร์ตในระหว่างการเริ่มต้นทำงาน เนื่องจากเครื่องสแกนการโหลดล่วงหน้าไม่อ่านแอตทริบิวต์ data-src
ในแบบเดียวกับที่แอตทริบิวต์ src
(หรือ srcset
) ทำให้ระบบไม่พบการอ้างอิงรูปภาพก่อนหน้านี้ ที่แย่กว่านั้นคือ รูปภาพเกิดความล่าช้าจากการโหลดจนถึงหลังที่ JavaScript ของเครื่องมือโหลดแบบ Lazy Loading จะดาวน์โหลด คอมไพล์ และเรียกใช้
![แผนภูมิ Waterfall ของเครือข่าย WebPageTest แสดงให้เห็นว่ารูปภาพที่โหลดแบบ Lazy Loading ในวิวพอร์ตในระหว่างการเริ่มต้นทำงานนั้นต้องมีความล่าช้าเนื่องจากเครื่องสแกนการโหลดล่วงหน้าของเบราว์เซอร์ไม่พบทรัพยากรรูปภาพ และจะโหลดเฉพาะเมื่อ JavaScript ที่จำเป็นสำหรับการโหลดแบบ Lazy Loading จึงจะโหลดได้ รูปภาพได้รับการค้นพบในภายหลังกว่าที่ควรจะเป็น](https://web.dev/static/articles/preload-scanner/image/a-webpagetest-network-wat-6e823c2a1d7a6.png?authuser=0&hl=th)
โดยอาจเป็นองค์ประกอบที่ใช้สำหรับ Largest Contentful Paint (LCP) โดยขึ้นอยู่กับขนาดของรูปภาพซึ่งอาจขึ้นอยู่กับขนาดของวิวพอร์ต เมื่อเครื่องสแกนการโหลดล่วงหน้าไม่สามารถดึงทรัพยากรรูปภาพล่วงหน้าได้ ซึ่งอาจเกิดขึ้นระหว่างจุดที่สไตล์ชีตของหน้าเว็บบล็อกการแสดงผล LCP เกิดปัญหา
วิธีแก้ไขคือการเปลี่ยนมาร์กอัปรูปภาพ
<img src="/sand-wasp.jpg" alt="Sand Wasp" width="384" height="255">
รูปแบบนี้เป็นรูปแบบที่เหมาะสมที่สุดสำหรับรูปภาพที่อยู่ในวิวพอร์ตระหว่างการเริ่มต้น เนื่องจากตัวสแกนการโหลดล่วงหน้าจะค้นหาและดึงทรัพยากรรูปภาพได้รวดเร็วยิ่งขึ้น
![แผนภูมิ Waterfall ของเครือข่าย WebPageTest ที่แสดงสถานการณ์การโหลดรูปภาพในวิวพอร์ตในระหว่างการเริ่มต้น รูปภาพไม่ได้โหลดแบบ Lazy Loading ซึ่งหมายความว่ารูปภาพดังกล่าวไม่ได้ขึ้นอยู่กับสคริปต์ในการโหลด ซึ่งหมายความว่าเครื่องสแกนการโหลดล่วงหน้าจะค้นพบได้เร็วขึ้น](https://web.dev/static/articles/preload-scanner/image/a-webpagetest-network-wat-5e2602b834b47.png?authuser=0&hl=th)
ผลลัพธ์ในตัวอย่างง่ายๆ นี้คือ LCP ดีขึ้น 100 มิลลิวินาทีในการเชื่อมต่อที่ช้า สิ่งนี้อาจดูไม่ใช่การปรับปรุงครั้งใหญ่ แต่เมื่อใดที่คุณเห็นว่าโซลูชันนี้เป็นการแก้ไขมาร์กอัปอย่างรวดเร็วและหน้าเว็บส่วนใหญ่มีความซับซ้อนมากกว่าตัวอย่างชุดนี้ ซึ่งหมายความว่าตัวเลือก LCP อาจต้องใช้แบนด์วิดท์กับทรัพยากรอื่นๆ จำนวนมาก การเพิ่มประสิทธิภาพในลักษณะนี้จึงมีความสำคัญเพิ่มมากขึ้น
ภาพพื้นหลัง CSS
โปรดทราบว่าตัวสแกนการโหลดล่วงหน้าของเบราว์เซอร์จะสแกนมาร์กอัป แต่จะไม่สแกนทรัพยากรประเภทอื่นๆ เช่น CSS ที่อาจเกี่ยวข้องกับการดึงข้อมูลรูปภาพที่พร็อพเพอร์ตี้ background-image
อ้างอิง
เบราว์เซอร์จะประมวลผล CSS เป็นโมเดลออบเจ็กต์ของตัวเองซึ่งเรียกว่า CSSOM เช่นเดียวกับ HTML หากพบทรัพยากรภายนอกในขณะที่สร้าง CSSOM ทรัพยากรเหล่านั้นจะขอ ณ เวลาที่ค้นพบ ไม่ใช่โดยเครื่องสแกนการโหลดล่วงหน้า
สมมติว่าตัวเลือก LCP ของหน้าเป็นองค์ประกอบที่มีพร็อพเพอร์ตี้ background-image
ของ CSS ต่อไปนี้คือสิ่งที่จะเกิดขึ้นเมื่อทรัพยากรโหลด
![แผนภูมิ Waterfall ของเครือข่าย WebPageTest ที่แสดงหน้าเว็บที่มีตัวเลือก LCP ซึ่งโหลดจาก CSS โดยใช้พร็อพเพอร์ตี้ภาพพื้นหลัง เนื่องจากรูปภาพตัวเลือก LCP อยู่ในประเภททรัพยากรที่เครื่องสแกนการโหลดล่วงหน้าของเบราว์เซอร์ตรวจสอบไม่ได้ ทรัพยากรจึงล่าช้าจากการโหลดจนกว่าจะมีการดาวน์โหลดและประมวลผล CSS ทำให้เวลาในการแสดงผลของผู้สมัคร LCP ล่าช้า](https://web.dev/static/articles/preload-scanner/image/a-webpagetest-network-wat-ca60b2c25fa52.png?authuser=0&hl=th)
background-image
ของ CSS (แถว 3) รูปภาพที่ส่งคำขอจะไม่เริ่มดึงข้อมูลจนกว่าโปรแกรมแยกวิเคราะห์ CSS พบรูปภาพดังกล่าว
ในกรณีนี้ ตัวสแกนการโหลดล่วงหน้าจึงไม่ได้ขัดแย้งกันมากนักเพราะไม่เกี่ยวข้อง อย่างไรก็ตาม หากตัวเลือก LCP บนหน้าเว็บมาจากพร็อพเพอร์ตี้ CSS background-image
ก็จะต้องโหลดรูปภาพนั้นล่วงหน้า โดยทำดังนี้
<!-- Make sure this is in the <head> below any
stylesheets, so as not to block them from loading -->
<link rel="preload" as="image" href="lcp-image.jpg">
คำแนะนำ rel=preload
นั้นมีขนาดเล็ก แต่จะช่วยให้เบราว์เซอร์ค้นพบรูปภาพได้เร็วขึ้นดังนี้
![แผนภูมิ Waterfall ของเครือข่าย WebPageTest ที่แสดงภาพพื้นหลัง CSS (ซึ่งเป็นตัวเลือก LCP) ที่โหลดเร็วขึ้นมากเนื่องจากการใช้คำแนะนำ rel=preload เวลา LCP ดีขึ้นประมาณ 250 มิลลิวินาที](https://web.dev/static/articles/preload-scanner/image/a-webpagetest-network-wat-8a47e283c7c66.png?authuser=0&hl=th)
background-image
ของ CSS (แถว 3) คำแนะนำ rel=preload
ช่วยให้เบราว์เซอร์ค้นพบรูปภาพได้เร็วขึ้นประมาณ 250 มิลลิวินาที
เมื่อใช้คำแนะนำ rel=preload
ระบบจะค้นพบตัวเลือก LCP ได้เร็วขึ้น ซึ่งจะลดเวลา LCP แม้ว่าคำแนะนำนี้จะช่วยแก้ปัญหานี้ได้ แต่ตัวเลือกที่ดีกว่าคือการประเมินว่าตัวเลือก LCP ของรูปภาพต้องโหลดจาก CSS หรือไม่ แท็ก <img>
ช่วยให้คุณควบคุมการโหลดรูปภาพที่เหมาะสำหรับวิวพอร์ตได้มากขึ้น ในขณะที่ตัวสแกนการโหลดล่วงหน้าจะค้นพบรูปภาพนั้นได้
แทรกทรัพยากรมากเกินไป
การแทรกในบรรทัดเป็นแนวทางปฏิบัติโดยวางแหล่งข้อมูลไว้ภายใน HTML คุณสามารถแทรกสไตล์ชีตไว้ในองค์ประกอบ <style>
, สคริปต์ในองค์ประกอบ <script>
และทรัพยากรเสมือนอื่นๆ ได้โดยใช้การเข้ารหัส base64
การใส่ทรัพยากรในหน้าอาจรวดเร็วกว่าการดาวน์โหลดทรัพยากรดังกล่าว เนื่องจากระบบจะไม่ส่งคำขอแยกต่างหากสำหรับทรัพยากรนั้นๆ ปุ่มนี้อยู่ในเอกสารโดยตรงและโหลดได้ทันที อย่างไรก็ตาม มีข้อเสียสำคัญดังนี้
- ในกรณีที่ไม่ได้แคช HTML และจะทำไม่ได้หากการตอบสนองของ HTML เป็นแบบไดนามิก ระบบจะไม่แคชทรัพยากรในบรรทัดนั้นเลย ปัญหานี้ส่งผลกระทบต่อประสิทธิภาพเนื่องจากทรัพยากรที่แทรกในบรรทัดไม่สามารถนำมาใช้ซ้ำได้
- แม้ว่าจะแคช HTML ได้ แต่ระบบจะไม่แชร์ทรัพยากรในบรรทัดระหว่างเอกสารต่างๆ ซึ่งจะลดประสิทธิภาพการแคชเมื่อเทียบกับไฟล์ภายนอกที่สามารถแคชและใช้ซ้ำได้ในต้นทางทั้งหมด
- กรณีที่แทรกในบรรทัดมากเกินไป จะทำให้เครื่องสแกนการโหลดล่วงหน้าไม่พบทรัพยากรภายหลังในเอกสาร เนื่องจากการดาวน์โหลดเนื้อหาที่เพิ่มขึ้นมาและในบรรทัดจะใช้เวลานานกว่านั้น
ดูหน้านี้เป็นตัวอย่าง ในบางเงื่อนไข ตัวเลือก LCP จะเป็นรูปภาพที่ด้านบนของหน้า และ CSS อยู่ในไฟล์แยกต่างหากซึ่งโหลดโดยองค์ประกอบ <link>
หน้าเว็บยังใช้แบบอักษรเว็บอีก 4 แบบซึ่งเราขอเป็นไฟล์แยกต่างหากจากทรัพยากร CSS
![แผนภูมิ Waterfall เครือข่าย WebPageTest ของหน้าเว็บที่มีไฟล์ CSS ภายนอกซึ่งมีแบบอักษร 4 แบบอ้างอิงอยู่ในนั้น เครื่องสแกนการโหลดล่วงหน้าจะตรวจพบรูปภาพตัวเลือก LCP ในระยะเวลาที่กำหนดไว้](https://web.dev/static/articles/preload-scanner/image/a-webpagetest-network-wat-ebe04c09d25a6.png?authuser=0&hl=th)
<img>
แต่ตรวจพบโดยตัวสแกนการโหลดล่วงหน้าเนื่องจาก CSS และแบบอักษรที่จำเป็นสำหรับการโหลดหน้าเว็บในทรัพยากรแยกกัน ซึ่งไม่ทำให้เครื่องสแกนการโหลดล่วงหน้าทำงาน
จะเกิดอะไรขึ้นหาก CSS และแบบอักษรทั้งหมดรวมอยู่ในทรัพยากร base64
![แผนภูมิ Waterfall เครือข่าย WebPageTest ของหน้าเว็บที่มีไฟล์ CSS ภายนอกซึ่งมีแบบอักษร 4 แบบอ้างอิงอยู่ในนั้น เครื่องสแกนการโหลดล่วงหน้าล่าช้าอย่างมากเมื่อพบรูปภาพ LCP](https://web.dev/static/articles/preload-scanner/image/a-webpagetest-network-wat-2cad0c33c7bdd.png?authuser=0&hl=th)
<img>
แต่อินไลน์ของ CSS และทรัพยากรแบบอักษร 4 รายการใน "" จะทำให้เครื่องสแกนการโหลดล่วงหน้าค้นพบรูปภาพไม่ได้จนกว่าจะดาวน์โหลดทรัพยากรเหล่านั้นเสร็จสมบูรณ์
ผลจากการแทรกในบรรทัดให้ผลเสียต่อ LCP ในตัวอย่างนี้และต่อประสิทธิภาพโดยทั่วไป เวอร์ชันของหน้าเว็บที่ไม่มีสิ่งใดแทรกในบรรทัดจะระบายสีรูปภาพ LCP ภายในประมาณ 3.5 วินาที หน้าเว็บที่แทรกทุกอย่างจะไม่แสดงรูปภาพ LCP จนกว่าจะผ่านมาเพียง 7 วินาที
ที่นี่มีอะไรน่าสนใจอีกมากมายนอกเหนือจากตัวสแกนการโหลดล่วงหน้า แบบอักษรในบรรทัดไม่ใช่กลยุทธ์ที่ดีเนื่องจาก Base64 เป็นรูปแบบที่ไม่มีประสิทธิภาพสำหรับทรัพยากรแบบไบนารี อีกปัจจัยหนึ่งคือทรัพยากรแบบอักษรภายนอกจะไม่ได้รับการดาวน์โหลด เว้นแต่ CSSOM จะพิจารณาว่าจำเป็นต้องใช้ เมื่อแบบอักษรดังกล่าวแทรกเป็น base64 ระบบจะดาวน์โหลดแบบอักษรนั้น ไม่ว่าต้องใช้สําหรับหน้าปัจจุบันหรือไม่
การโหลดล่วงหน้าจะช่วยปรับปรุงสิ่งต่างๆ ที่นี่ได้ไหม เอาสิ คุณสามารถโหลดรูปภาพ LCP ล่วงหน้าและลดเวลา LCP แต่การขยาย HTML ที่อาจแคชไม่ได้ด้วยทรัพยากรแบบแทรกในบรรทัดจะส่งผลเสียต่อประสิทธิภาพในทางลบอื่นๆ First Contentful Paint (FCP) ก็ได้รับผลกระทบจากรูปแบบนี้เช่นกัน ในเวอร์ชันของหน้าเว็บที่ไม่มีข้อมูลในบรรทัด FCP จะมีความยาวประมาณ 2.7 วินาที ในเวอร์ชันที่ทุกอย่างในบรรทัด FCP มีความยาวประมาณ 5.8 วินาที
โปรดระมัดระวังเมื่อใส่ข้อมูลลงใน HTML โดยเฉพาะทรัพยากรที่เข้ารหัส base64 โดยทั่วไปเราไม่แนะนำให้ทำ ยกเว้นแหล่งข้อมูลที่มีขนาดเล็กมาก แทรกในบรรทัดให้น้อยที่สุดเท่าที่ทำได้ เพราะการอินไลน์มากเกินไปจะทำให้มีการใช้ไฟ
การแสดงผลมาร์กอัปด้วย JavaScript ฝั่งไคลเอ็นต์
แน่นอนว่า "JavaScript ส่งผลต่อความเร็วของหน้าอย่างมาก นักพัฒนาแอปไม่เพียงต้องพึ่งพาแพลตฟอร์มมอบการโต้ตอบเท่านั้น แต่ยังมีแนวโน้มที่จะพึ่งพาฟีเจอร์นี้เพื่อแสดงเนื้อหาด้วย ซึ่งจะช่วยให้นักพัฒนาแอปได้รับประสบการณ์ที่ดีขึ้นในบางด้าน แต่สิทธิประโยชน์สำหรับนักพัฒนาซอฟต์แวร์ก็ไม่ได้แปลออกมาเป็นประโยชน์สำหรับผู้ใช้เสมอไป
รูปแบบหนึ่งที่เอาชนะตัวสแกนการโหลดล่วงหน้าได้คือการแสดงผลมาร์กอัปด้วย JavaScript ฝั่งไคลเอ็นต์ ดังนี้
![การแสดงวิดีโอตามลำดับขั้นในเครือข่าย WebPageTest ซึ่งแสดงหน้าเว็บพื้นฐานที่มีรูปภาพและข้อความที่แสดงผลทั้งหมดบนไคลเอ็นต์ใน JavaScript เนื่องจากมาร์กอัปอยู่ใน JavaScript ตัวสแกนการโหลดล่วงหน้าจึงตรวจไม่พบทรัพยากรใดๆ ทรัพยากรทั้งหมดล่าช้าด้วยเช่นกันเนื่องจากเครือข่ายและเวลาในการประมวลผลที่เฟรมเวิร์ก JavaScript ต้องใช้](https://web.dev/static/articles/preload-scanner/image/a-webpagetest-network-wat-9a4b1c22879f5.png?authuser=0&hl=th)
เมื่อมีเพย์โหลดของมาร์กอัปอยู่ในและแสดงผลโดย JavaScript ทั้งหมดในเบราว์เซอร์ ทรัพยากรใดๆ ในมาร์กอัปนั้นจะมองไม่เห็นโดยตัวสแกนการโหลดล่วงหน้า การดำเนินการดังกล่าวจะทำให้การค้นหาทรัพยากรที่สำคัญล่าช้าลง ซึ่งจะส่งผลต่อ LCP อย่างแน่นอน ในกรณีของตัวอย่างเหล่านี้ คำขอรูปภาพ LCP จะล่าช้าอย่างมากเมื่อเทียบกับประสบการณ์การแสดงผลโดยเซิร์ฟเวอร์ที่เทียบเท่าซึ่งไม่จำเป็นต้องใช้ JavaScript ในการแสดงผล
ซึ่งเบี่ยงเบนความสนใจไปเล็กน้อยจากบทความนี้ แต่ผลของการแสดงมาร์กอัปบนไคลเอ็นต์ไปไกลกว่าการเอาชนะตัวสแกนการโหลดล่วงหน้า อย่างแรกคือการเริ่มใช้ JavaScript เพื่อขับเคลื่อนประสบการณ์ที่ไม่ต้องใช้ ทำให้ใช้เวลาประมวลผลที่ไม่จำเป็นซึ่งอาจส่งผลต่อการโต้ตอบกับ Next Paint (INP)
นอกจากนี้ การแสดงผลมาร์กอัปจำนวนมากในไคลเอ็นต์มีแนวโน้มที่จะสร้างงานที่ใช้เวลานานมากกว่าเมื่อเทียบกับจำนวนมาร์กอัปที่ส่งจากเซิร์ฟเวอร์ นอกเหนือจากการประมวลผลเพิ่มเติมจาก JavaScript แล้ว คือการที่เบราว์เซอร์สตรีมมาร์กอัปจากเซิร์ฟเวอร์และแบ่งการแสดงผลเป็นส่วนๆ เพื่อที่จะหลีกเลี่ยงงานที่ใช้เวลานาน ในทางกลับกัน มาร์กอัปที่แสดงโดยไคลเอ็นต์จะจัดการเป็นงานแบบโมโนลิธเดียว ซึ่งอาจส่งผลต่อเมตริกการตอบสนองของหน้าเว็บ เช่น เวลาการบล็อกทั้งหมด (TBT) หรือ First Input Delay (FID) นอกเหนือจาก INP
วิธีแก้ไขในสถานการณ์นี้ขึ้นอยู่กับคำตอบของคำถามนี้ มีเหตุผลอื่นไหมที่เซิร์ฟเวอร์ไม่สามารถให้มาร์กอัปของหน้าเว็บแทนที่จะแสดงผลบนไคลเอ็นต์ หากคำตอบคือ "ไม่" ควรพิจารณาการแสดงผลฝั่งเซิร์ฟเวอร์ (SSR) หรือมาร์กอัปที่สร้างขึ้นแบบคงที่เมื่อเป็นไปได้ เนื่องจากจะช่วยให้เครื่องสแกนการโหลดล่วงหน้าค้นพบและดึงทรัพยากรสำคัญตามโอกาสได้ล่วงหน้า
หากหน้าเว็บต้องการใช้ JavaScript เพื่อแนบฟังก์ชันการทำงานกับมาร์กอัปบางส่วนของหน้าเว็บ คุณจะยังคงทำได้โดยใช้ SSR ไม่ว่าจะใช้ JavaScript แบบวานิลลาหรือแบบ Hydration เพื่อการใช้งานที่ดีที่สุดทั้ง 2 ด้าน
ช่วยให้เครื่องสแกนการโหลดล่วงหน้าช่วยคุณได้
ตัวสแกนการโหลดล่วงหน้าเป็นการเพิ่มประสิทธิภาพเบราว์เซอร์ที่มีประสิทธิภาพสูง ซึ่งช่วยให้หน้าเว็บโหลดเร็วขึ้นในช่วงเริ่มต้น คุณไม่เพียงทำให้การพัฒนาง่ายขึ้นสำหรับตัวคุณเองเท่านั้น แต่ยังเป็นการสร้างประสบการณ์ของผู้ใช้ที่ดียิ่งขึ้นซึ่งจะให้ผลลัพธ์ที่ดีขึ้นในเมตริกหลายรายการ รวมถึง Web Vitals ด้วยการหลีกเลี่ยงรูปแบบที่ทำให้แอปไม่สามารถค้นพบทรัพยากรที่สำคัญล่วงหน้าได้
กล่าวโดยสรุปคือ สิ่งที่ควรได้จากโพสต์นี้มีดังนี้
- ตัวสแกนการโหลดล่วงหน้าของเบราว์เซอร์คือ โปรแกรมแยกวิเคราะห์ HTML รองที่จะสแกนก่อนโปรแกรมหลักหากระบบบล็อกไว้เพื่อให้ค้นพบทรัพยากรที่ดึงข้อมูลได้เร็วขึ้นตามโอกาส
- เครื่องสแกนการโหลดล่วงหน้าจะค้นหาทรัพยากรซึ่งไม่มีอยู่ในมาร์กอัปที่เซิร์ฟเวอร์ให้ไว้ในคำขอการนำทางเริ่มต้น ตัวอย่างที่อาจลบล้างตัวสแกนการโหลดล่วงหน้าอาจรวมถึง (แต่ไม่จำกัดเพียง) สิ่งต่อไปนี้
- การแทรกทรัพยากรลงใน DOM ด้วย JavaScript อาจเป็นสคริปต์ รูปภาพ สไตล์ชีต หรืออื่นๆ ที่น่าจะมีประสิทธิภาพดีกว่าในเพย์โหลดมาร์กอัปเริ่มต้นจากเซิร์ฟเวอร์
- การโหลดรูปภาพครึ่งหน้าบนหรือ iframe แบบ Lazy Loading โดยใช้โซลูชัน JavaScript
- กําลังแสดงมาร์กอัปบนไคลเอ็นต์ที่อาจมีการอ้างอิงไปยังทรัพยากรย่อยของเอกสารโดยใช้ JavaScript
- ตัวสแกนการโหลดล่วงหน้าจะสแกนเฉพาะ HTML เท่านั้น แต่จะไม่ตรวจสอบเนื้อหาของทรัพยากรอื่นๆ โดยเฉพาะ CSS ที่อาจรวมถึงการอ้างอิงเนื้อหาสําคัญ ซึ่งรวมถึงตัวเลือก LCP
หากคุณไม่สามารถหลีกเลี่ยงรูปแบบที่ส่งผลเสียต่อความสามารถของตัวสแกนการโหลดล่วงหน้าในการเพิ่มประสิทธิภาพของการโหลดได้ ให้พิจารณาคำแนะนำด้านทรัพยากรของ rel=preload
หากคุณใช้ rel=preload
ให้ทดสอบในเครื่องมือในห้องทดลองเพื่อให้แน่ใจว่าให้ผลตามที่ต้องการ สุดท้าย อย่าโหลดทรัพยากรมากเกินไป เพราะเมื่อคุณจัดลำดับความสำคัญแล้ว ทุกอย่างจะไม่เกิดขึ้น
แหล่งข้อมูล
- "สคริปต์แบบไม่พร้อมกัน" ที่มีการแทรกสคริปต์ถือว่าเป็นอันตราย
- วิธีที่ตัวโหลดล่วงหน้าของเบราว์เซอร์ทำให้หน้าเว็บโหลดเร็วขึ้น
- โหลดเนื้อหาสำคัญไว้ล่วงหน้าเพื่อปรับปรุงความเร็วในการโหลด
- สร้างการเชื่อมต่อเครือข่ายตั้งแต่เนิ่นๆ เพื่อปรับปรุงความเร็วหน้าเว็บที่รับรู้
- การเพิ่มประสิทธิภาพ Largest Contentful Paint
รูปภาพหลักจาก Unsplash โดย Mohammad Rahmani