อย่าต่อสู้กับตัวสแกนการโหลดล่วงหน้าของเบราว์เซอร์

ดูว่าเครื่องสแกนการโหลดล่วงหน้าของเบราว์เซอร์คืออะไร ช่วยเพิ่มประสิทธิภาพได้อย่างไร และวิธีหลีกเลี่ยงเครื่องสแกนนี้

แง่มุมหนึ่งที่มักถูกมองข้ามในการเพิ่มประสิทธิภาพ Page Speed คือการทำความเข้าใจเกี่ยวกับส่วนประกอบภายในของเบราว์เซอร์ เบราว์เซอร์จะทำการเพิ่มประสิทธิภาพบางอย่างเพื่อปรับปรุงประสิทธิภาพในลักษณะที่เราในฐานะนักพัฒนาซอฟต์แวร์ทำไม่ได้ แต่จะทำได้ก็ต่อเมื่อการเพิ่มประสิทธิภาพเหล่านั้นไม่ถูกขัดขวางโดยไม่ตั้งใจ

การเพิ่มประสิทธิภาพเบราว์เซอร์ภายในอย่างหนึ่งที่ควรทราบคือเครื่องสแกนการโหลดล่วงหน้าของเบราว์เซอร์ โพสต์นี้จะอธิบายวิธีการทำงานของเครื่องสแกนการโหลดล่วงหน้า และที่สำคัญกว่านั้นคือวิธีที่คุณหลีกเลี่ยงไม่ให้เครื่องสแกนนี้ทำงาน

เครื่องสแกนก่อนโหลดคืออะไร

เบราว์เซอร์ทุกตัวมีตัวแยกวิเคราะห์ HTML หลักที่สร้างโทเค็นมาร์กอัปดิบและประมวลผลเป็นโมเดลออบเจ็กต์ การทำงานนี้จะดำเนินต่อไปอย่างราบรื่นจนกว่าตัวแยกวิเคราะห์จะหยุดชั่วคราวเมื่อพบทรัพยากรที่บล็อก เช่น สไตล์ชีตที่โหลดด้วยองค์ประกอบ <link> หรือสคริปต์ที่โหลดด้วยองค์ประกอบ <script> โดยไม่มีแอตทริบิวต์ async หรือ defer

แผนภาพโปรแกรมแยกวิเคราะห์ HTML
รูปที่ 1: แผนภาพแสดงวิธีบล็อกตัวแยกวิเคราะห์ HTML หลักของเบราว์เซอร์ ในกรณีนี้ ตัวแยกวิเคราะห์จะพบองค์ประกอบ <link> สำหรับไฟล์ CSS ภายนอก ซึ่งจะบล็อกไม่ให้เบราว์เซอร์แยกวิเคราะห์ส่วนที่เหลือของเอกสาร หรือแม้แต่แสดงผลเอกสารจนกว่าจะดาวน์โหลดและแยกวิเคราะห์ CSS

ในกรณีของไฟล์ CSS ระบบจะบล็อกการแสดงผลเพื่อป้องกันเนื้อหาที่ไม่มีสไตล์ปรากฏชั่วคราว (FOUC) ซึ่งเป็นกรณีที่เวอร์ชันที่ไม่มีสไตล์ของหน้าเว็บปรากฏให้เห็นในระยะเวลาสั้นๆ ก่อนที่จะมีการใช้สไตล์กับหน้าเว็บนั้น

หน้าแรกของ web.dev ในสถานะที่ไม่ได้จัดรูปแบบ (ซ้าย) และสถานะที่จัดรูปแบบแล้ว (ขวา)
รูปที่ 2: ตัวอย่างจำลองของ FOUC ทางด้านซ้ายคือหน้าแรกของ web.dev ที่ไม่มีสไตล์ ทางด้านขวาคือหน้าเดียวกันที่ใช้รูปแบบ สถานะที่ไม่มีการจัดรูปแบบอาจเกิดขึ้นในพริบตาหากเบราว์เซอร์ไม่บล็อกการแสดงผลขณะที่ดาวน์โหลดและประมวลผลชีตสไตล์

นอกจากนี้ เบราว์เซอร์ยังบล็อกการแยกวิเคราะห์และการแสดงผลหน้าเว็บเมื่อพบองค์ประกอบ <script> ที่ไม่มีแอตทริบิวต์ defer หรือ async

สาเหตุคือเบราว์เซอร์ไม่สามารถทราบได้อย่างแน่ชัดว่าสคริปต์ที่กำหนดจะแก้ไข DOM ในขณะที่ตัวแยกวิเคราะห์ HTML หลักยังคงทำงานอยู่หรือไม่ ด้วยเหตุนี้จึงเป็นแนวทางปฏิบัติทั่วไปในการโหลด JavaScript ที่ส่วนท้ายของเอกสารเพื่อให้ผลกระทบของการแยกวิเคราะห์และการแสดงผลที่ถูกบล็อกลดลง

นี่เป็นเหตุผลที่ดีที่เบราว์เซอร์ควรบล็อกทั้งการแยกวิเคราะห์และการแสดงผล อย่างไรก็ตาม การบล็อกขั้นตอนสำคัญเหล่านี้ไม่เป็นที่พึงประสงค์ เนื่องจากอาจทำให้การแสดงหยุดชะงักด้วยการหน่วงเวลาการค้นพบทรัพยากรสำคัญอื่นๆ โชคดีที่เบราว์เซอร์พยายามอย่างเต็มที่เพื่อลดปัญหาเหล่านี้ด้วยการใช้ตัวแยกวิเคราะห์ HTML รองที่เรียกว่าเครื่องสแกนการโหลดล่วงหน้า

แผนภาพของทั้งตัวแยกวิเคราะห์ HTML หลัก (ซ้าย) และเครื่องสแกนการโหลดล่วงหน้า (ขวา) ซึ่งเป็นตัวแยกวิเคราะห์ HTML รอง
รูปที่ 3: แผนภาพที่แสดงวิธีการทำงานของเครื่องสแกนการโหลดล่วงหน้าควบคู่ไปกับตัวแยกวิเคราะห์ HTML หลักเพื่อโหลดชิ้นงานแบบคาดการณ์ ในกรณีนี้ ตัวแยกวิเคราะห์ HTML หลักจะถูกบล็อกขณะโหลดและประมวลผล CSS ก่อนที่จะเริ่มประมวลผลมาร์กอัปรูปภาพในองค์ประกอบ <body> ได้ แต่เครื่องสแกนการโหลดล่วงหน้าจะดูมาร์กอัปดิบเพื่อค้นหาแหล่งข้อมูลรูปภาพนั้นและเริ่มโหลดก่อนที่จะยกเลิกการบล็อกตัวแยกวิเคราะห์ HTML หลัก

บทบาทของเครื่องสแกนการโหลดล่วงหน้าเป็นการคาดการณ์ ซึ่งหมายความว่าเครื่องสแกนจะตรวจสอบมาร์กอัปดิบเพื่อค้นหาทรัพยากรที่จะดึงข้อมูลอย่างฉวยโอกาสก่อนที่ตัวแยกวิเคราะห์ HTML หลักจะค้นพบทรัพยากรเหล่านั้น

วิธีดูว่าเครื่องสแกนการโหลดล่วงหน้าทำงานอยู่หรือไม่

เครื่องสแกนการโหลดล่วงหน้ามีเนื่องจากการแสดงผลและการแยกวิเคราะห์ถูกบล็อก หากไม่มีปัญหาด้านประสิทธิภาพ 2 อย่างนี้ เครื่องมือสแกนการโหลดล่วงหน้าก็คงไม่มีประโยชน์มากนัก กุญแจสำคัญในการพิจารณาว่าหน้าเว็บได้รับประโยชน์จากตัวสแกนการโหลดล่วงหน้าหรือไม่นั้นขึ้นอยู่กับปรากฏการณ์การบล็อกเหล่านี้ โดยคุณสามารถเพิ่มการหน่วงเวลาเทียมสำหรับคำขอเพื่อดูว่าเครื่องสแกนการโหลดล่วงหน้าทำงานที่ใด

ดูหน้านี้ที่มีข้อความและรูปภาพพื้นฐานพร้อมสไตล์ชีตเป็นตัวอย่าง เนื่องจากไฟล์ CSS บล็อกทั้งการแสดงผลและการแยกวิเคราะห์ คุณจึงทำให้สไตล์ชีตเกิดความล่าช้าเทียม 2 วินาทีผ่านบริการพร็อกซี ความล่าช้านี้จะช่วยให้คุณเห็นได้ง่ายขึ้นในลำดับการโหลดเครือข่ายว่าเครื่องสแกนการโหลดล่วงหน้าทำงานอยู่ที่ใด

แผนภูมิลำดับชั้นของเครือข่าย WebPageTest แสดงการหน่วงเวลาเทียม 2 วินาทีที่กำหนดในสไตล์ชีต
รูปที่ 4: WebPageTest แผนภาพน้ำตกของเครือข่ายของหน้าเว็บที่เรียกใช้ใน Chrome บนอุปกรณ์เคลื่อนที่ผ่านการเชื่อมต่อ 3G จำลอง แม้ว่าสไตล์ชีตจะถูกหน่วงเวลาเทียมผ่านพร็อกซีเป็นเวลา 2 วินาทีก่อนที่จะเริ่มโหลด แต่เครื่องสแกนการโหลดล่วงหน้าจะค้นพบรูปภาพที่อยู่ในเพย์โหลดมาร์กอัปในภายหลัง

ดังที่เห็นในวอเตอร์ฟอล ตัวสแกนการโหลดล่วงหน้าจะค้นพบองค์ประกอบ <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 นี้แสดงการสแกนการโหลดล่วงหน้าที่ล้มเหลวเมื่อมีการแทรกสคริปต์
รูปที่ 5: แผนภูมิลำดับการทำงานของเครือข่าย WebPageTest ของหน้าเว็บที่เรียกใช้ใน Chrome บนอุปกรณ์เคลื่อนที่ผ่านการเชื่อมต่อ 3G จำลอง หน้าเว็บมีชีตสไตล์ชีตเดียวและสคริปต์ async ที่แทรก ตัวสแกนการโหลดล่วงหน้าไม่พบสคริปต์ในระหว่างระยะการบล็อกการแสดงผล เนื่องจากมีการแทรกสคริปต์ในไคลเอ็นต์

มาดูสิ่งที่เกิดขึ้นกัน

  1. ที่ 0 วินาที ระบบจะขอเอกสารหลัก
  2. ที่ 1.4 วินาที ไบต์แรกของคำขอการนำทางจะมาถึง
  3. ที่ 2.0 วินาที ระบบจะขอ CSS และรูปภาพ
  4. เนื่องจากตัวแยกวิเคราะห์ถูกบล็อกไม่ให้โหลดสไตล์ชีตและ JavaScript แบบอินไลน์ที่แทรกสคริปต์ async จะมาหลังจากสไตล์ชีตนั้นที่ 2.6 วินาที ฟังก์ชันการทำงานที่สคริปต์นั้นมีให้จึงไม่พร้อมใช้งานในทันที

ซึ่งเป็นวิธีที่ไม่ดีนักเนื่องจากคำขอสคริปต์จะเกิดขึ้นหลังจากที่ดาวน์โหลดสไตล์ชีตเสร็จแล้วเท่านั้น ซึ่งจะทำให้สคริปต์ทำงานช้าลง ในทางตรงกันข้าม เนื่องจากองค์ประกอบ <img> ค้นพบได้ในมาร์กอัปที่เซิร์ฟเวอร์ระบุไว้ เครื่องสแกนการโหลดล่วงหน้าจึงค้นพบองค์ประกอบนี้

แล้วจะเกิดอะไรขึ้นหากคุณใช้แท็ก <script> ปกติที่มีแอตทริบิวต์ async แทนที่จะแทรกสคริปต์ลงใน DOM

<script src="/yall.min.js" async></script>

ผลลัพธ์ที่ได้มีดังนี้

ลำดับการโหลดทรัพยากรบนเครือข่ายของ WebPageTest แสดงให้เห็นว่าตัวสแกนการโหลดล่วงหน้าของเบราว์เซอร์ยังคงค้นพบสคริปต์แบบไม่พร้อมกันที่โหลดโดยใช้องค์ประกอบสคริปต์ HTML แม้ว่าตัวแยกวิเคราะห์ HTML หลักของเบราว์เซอร์จะถูกบล็อกขณะดาวน์โหลดและประมวลผลสไตล์ชีตก็ตาม
รูปที่ 6: แผนภูมิลำดับการทำงานของเครือข่าย WebPageTest ของหน้าเว็บที่เรียกใช้ใน Chrome บนอุปกรณ์เคลื่อนที่ผ่านการเชื่อมต่อ 3G จำลอง หน้าเว็บมีสไตล์ชีตเดียวและองค์ประกอบ async <script> เดียว ตัวสแกนการโหลดล่วงหน้าจะค้นพบสคริปต์ในระหว่างระยะการบล็อกการแสดงผล และโหลดพร้อมกับ CSS

คุณอาจอยากแนะนำว่าปัญหาเหล่านี้แก้ไขได้โดยใช้ rel=preload วิธีนี้ใช้ได้แน่นอน แต่ก็อาจมีผลข้างเคียงบางอย่าง ท้ายที่สุดแล้ว เหตุใดจึงต้องใช้ rel=preload เพื่อแก้ไขปัญหาที่หลีกเลี่ยงได้ด้วยการไม่แทรกองค์ประกอบ <script> ลงใน DOM

Waterfall ของ WebPageTest แสดงให้เห็นว่าใช้คำใบ้ทรัพยากร rel=preload เพื่อส่งเสริมการค้นพบสคริปต์ที่แทรกแบบไม่พร้อมกันได้อย่างไร แม้ว่าอาจมีผลข้างเคียงที่ไม่คาดคิดก็ตาม
รูปที่ 7: แผนภูมิลำดับชั้นของเครือข่าย WebPageTest ของหน้าเว็บที่เรียกใช้ใน Chrome บนอุปกรณ์เคลื่อนที่ผ่านการเชื่อมต่อ 3G จำลอง หน้าเว็บมีชีตสไตล์ชีตเดียวและสคริปต์ 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 จะนำคำนำหน้า data- ออก ซึ่งหมายความว่าในตัวอย่างก่อนหน้า data-src จะกลายเป็น src การอัปเดตนี้จะแจ้งให้เบราว์เซอร์ดึงข้อมูลทรัพยากร

รูปแบบนี้ไม่ก่อให้เกิดปัญหาจนกว่าจะมีการนำไปใช้กับรูปภาพที่อยู่ในวิวพอร์ตระหว่างการเริ่มต้น เนื่องจากเครื่องสแกนการโหลดล่วงหน้าไม่ได้อ่านแอตทริบิวต์ data-src ในลักษณะเดียวกับที่อ่านแอตทริบิวต์ src (หรือ srcset) จึงไม่พบการอ้างอิงรูปภาพก่อนหน้านี้ ที่แย่กว่านั้นคือรูปภาพจะโหลดช้าจนกว่า JavaScript ของ Lazy Loader จะดาวน์โหลด คอมไพล์ และเรียกใช้หลังจาก

แผนภูมิลำดับชั้นของเครือข่าย WebPageTest แสดงให้เห็นว่ารูปภาพที่โหลดแบบ Lazy Loading ซึ่งอยู่ใน Viewport ระหว่างการเริ่มต้นระบบจะล่าช้าอย่างหลีกเลี่ยงไม่ได้ เนื่องจากเครื่องมือสแกนการโหลดล่วงหน้าของเบราว์เซอร์ไม่พบทรัพยากรรูปภาพ และจะโหลดเมื่อ JavaScript ที่จำเป็นต่อการทำงานของ Lazy Loading โหลดเท่านั้น ระบบพบรูปภาพช้ากว่าที่ควรจะเป็นมาก
รูปที่ 8: แผนภูมิลำดับการทำงานของเครือข่าย WebPageTest ของหน้าเว็บที่เรียกใช้ใน Chrome บนอุปกรณ์เคลื่อนที่ผ่านการเชื่อมต่อ 3G จำลอง ระบบโหลดทรัพยากรรูปภาพแบบ Lazy Loading โดยไม่จำเป็น แม้ว่ารูปภาพจะแสดงในวิวพอร์ตระหว่างการเริ่มต้นใช้งานก็ตาม ซึ่งจะทำให้เครื่องสแกนการโหลดล่วงหน้าใช้งานไม่ได้และทำให้เกิดความล่าช้าโดยไม่จำเป็น

รูปภาพอาจเป็นองค์ประกอบที่เข้าเกณฑ์สำหรับ Largest Contentful Paint (LCP) ทั้งนี้ขึ้นอยู่กับขนาดของรูปภาพ ซึ่งอาจขึ้นอยู่กับขนาดของวิวพอร์ต เมื่อตัวสแกนการโหลดล่วงหน้าไม่สามารถดึงทรัพยากรรูปภาพล่วงหน้าได้(อาจเป็นช่วงที่สไตล์ชีตของหน้าเว็บบล็อกการแสดงผล) LCP จะได้รับผลกระทบ

วิธีแก้คือการเปลี่ยนมาร์กอัปรูปภาพโดยทำดังนี้

<img src="/sand-wasp.jpg" alt="Sand Wasp" width="384" height="255">

ซึ่งเป็นรูปแบบที่เหมาะสมที่สุดสำหรับรูปภาพที่อยู่ใน Viewport ระหว่างการเริ่มต้น เนื่องจากเครื่องสแกนการโหลดล่วงหน้าจะค้นพบและดึงข้อมูลทรัพยากรรูปภาพได้เร็วขึ้น

แผนภูมิลำดับขั้นของเครือข่าย WebPageTest ที่แสดงสถานการณ์การโหลดรูปภาพในวิวพอร์ตระหว่างการเริ่มต้น รูปภาพไม่ได้โหลดแบบ Lazy Loading ซึ่งหมายความว่ารูปภาพไม่ได้ขึ้นอยู่กับสคริปต์ในการโหลด ซึ่งหมายความว่าเครื่องมือสแกนการโหลดล่วงหน้าจะค้นพบรูปภาพได้เร็วขึ้น
รูปที่ 9: แผนภูมิลำดับการทำงานของเครือข่าย WebPageTest ของหน้าเว็บที่เรียกใช้ใน Chrome บนอุปกรณ์เคลื่อนที่ผ่านการเชื่อมต่อ 3G จำลอง ตัวสแกนการโหลดล่วงหน้าจะค้นพบทรัพยากรรูปภาพก่อนที่ CSS และ JavaScript จะเริ่มโหลด ซึ่งช่วยให้เบราว์เซอร์เริ่มโหลดได้ก่อน

ผลลัพธ์ในตัวอย่างที่เรียบง่ายนี้คือการปรับปรุง LCP 100 มิลลิวินาทีในการเชื่อมต่อที่ช้า แม้ว่าการปรับปรุงนี้อาจดูไม่มากนัก แต่ก็ถือว่าเป็นการปรับปรุงที่สำคัญเมื่อพิจารณาว่าโซลูชันนี้เป็นการแก้ไขมาร์กอัปอย่างรวดเร็ว และหน้าเว็บส่วนใหญ่มีความซับซ้อนมากกว่าชุดตัวอย่างนี้ ซึ่งหมายความว่าองค์ประกอบที่ถือเป็น LCP อาจต้องแย่งแบนด์วิดท์กับทรัพยากรอื่นๆ อีกมากมาย การเพิ่มประสิทธิภาพเช่นนี้จึงมีความสำคัญมากขึ้นเรื่อยๆ

รูปภาพพื้นหลัง CSS

โปรดทราบว่าตัวสแกนการโหลดล่วงหน้าของเบราว์เซอร์จะสแกนมาร์กอัป โดยจะไม่สแกนทรัพยากรประเภทอื่นๆ เช่น CSS ซึ่งอาจเกี่ยวข้องกับการดึงข้อมูลรูปภาพที่อ้างอิงโดยพร็อพเพอร์ตี้ background-image

เช่นเดียวกับ HTML เบราว์เซอร์จะประมวลผล CSS เป็นโมเดลออบเจ็กต์ของตัวเอง ซึ่งเรียกว่า CSSOM หากพบทรัพยากรภายนอกขณะสร้าง CSSOM ระบบจะขอทรัพยากรเหล่านั้นในเวลาที่พบ ไม่ใช่โดยเครื่องสแกนการโหลดล่วงหน้า

สมมติว่าองค์ประกอบที่อาจเป็น LCP ของหน้าเว็บคือองค์ประกอบที่มีพร็อพเพอร์ตี้ CSS background-image สิ่งที่จะเกิดขึ้นเมื่อโหลดทรัพยากรมีดังนี้

แผนภูมิลำดับการโหลดทรัพยากรบนเครือข่ายของ WebPageTest ที่แสดงหน้าเว็บที่มีองค์ประกอบที่อาจเป็น LCP ซึ่งโหลดจาก CSS โดยใช้พร็อพเพอร์ตี้ background-image เนื่องจากรูปภาพที่ถือเป็น LCP อยู่ในประเภททรัพยากรที่เครื่องสแกนการโหลดล่วงหน้าของเบราว์เซอร์ตรวจสอบไม่ได้ ระบบจึงหน่วงเวลาการโหลดทรัพยากรจนกว่าจะดาวน์โหลดและประมวลผล CSS เสร็จ ซึ่งจะทำให้เวลาในการแสดงผลของรูปภาพที่ถือเป็น LCP ล่าช้า
รูปที่ 10: แผนภูมิ Waterfall ของเครือข่าย WebPageTest ของหน้าเว็บที่เรียกใช้ใน Chrome บนอุปกรณ์เคลื่อนที่ผ่านการเชื่อมต่อ 3G จำลอง องค์ประกอบที่มีพร็อพเพอร์ตี้ CSS background-image (แถวที่ 3) คือองค์ประกอบที่อาจเป็น LCP ของหน้าเว็บ รูปภาพที่ขอจะไม่เริ่มดึงข้อมูลจนกว่าตัวแยกวิเคราะห์ CSS จะพบ

ในกรณีนี้ เครื่องสแกนการโหลดล่วงหน้าไม่ได้ถูกปิดใช้งาน แต่ไม่ได้เกี่ยวข้อง อย่างไรก็ตาม หากองค์ประกอบที่อาจเป็น LCP ในหน้าเว็บมาจากbackground-imageพร็อพเพอร์ตี้ CSS คุณจะต้องโหลดรูปภาพนั้นล่วงหน้า

<!-- 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คำใบ้ดังกล่าวมีขนาดเล็ก แต่ช่วยให้เบราว์เซอร์ค้นพบรูปภาพได้เร็วกว่าปกติ

แผนภูมิลำดับขั้นของเครือข่าย WebPageTest แสดงภาพพื้นหลัง CSS (ซึ่งเป็น LCP ที่มีสิทธิ์) ที่โหลดได้เร็วกว่ามากเนื่องจากมีการใช้คำแนะนำ rel=preload เวลา LCP จะดีขึ้นประมาณ 250 มิลลิวินาที
รูปที่ 11: แผนภูมิน้ำตกของเครือข่าย WebPageTest ของหน้าเว็บที่เรียกใช้ใน Chrome บนอุปกรณ์เคลื่อนที่ผ่านการเชื่อมต่อ 3G จำลอง องค์ประกอบที่มีพร็อพเพอร์ตี้ CSS background-image (แถวที่ 3) คือองค์ประกอบที่อาจเป็น LCP ของหน้าเว็บ คำใบ้ rel=preload ช่วยให้เบราว์เซอร์ค้นพบรูปภาพได้เร็วขึ้นประมาณ 250 มิลลิวินาทีเมื่อเทียบกับการไม่มีคำใบ้

คำแนะนำ rel=preload จะช่วยให้พบองค์ประกอบที่อาจเป็น LCP ได้เร็วขึ้น ซึ่งจะช่วยลดเวลา LCP แม้ว่าคำแนะนำดังกล่าวจะช่วยแก้ไขปัญหานี้ได้ แต่ตัวเลือกที่ดีกว่าอาจเป็นการประเมินว่าองค์ประกอบ LCP ที่เป็นรูปภาพต้องโหลดจาก CSS หรือไม่ แท็ก <img> จะช่วยให้คุณควบคุมการโหลดรูปภาพที่เหมาะสมกับวิวพอร์ตได้มากขึ้น พร้อมทั้งอนุญาตให้เครื่องสแกนการโหลดล่วงหน้าค้นพบรูปภาพนั้น

การแทรกทรัพยากรมากเกินไป

การแทรกในบรรทัดเป็นแนวทางปฏิบัติที่วางทรัพยากรไว้ภายใน HTML คุณสามารถแทรกสไตล์ชีตในองค์ประกอบ <style> สคริปต์ในองค์ประกอบ <script> และแทบทุกทรัพยากรอื่นๆ โดยใช้การเข้ารหัส base64

การแทรกทรัพยากรในบรรทัดอาจเร็วกว่าการดาวน์โหลด เนื่องจากไม่มีการออกคำขอแยกต่างหากสำหรับทรัพยากร โดยจะอยู่ในเอกสารและโหลดได้ทันที แต่ก็มีข้อเสียที่สำคัญดังนี้

  • หากคุณไม่ได้แคช HTML (และทำไม่ได้หากการตอบกลับ HTML เป็นแบบไดนามิก) ระบบจะไม่แคชทรัพยากรแบบอินไลน์ ซึ่งจะส่งผลต่อประสิทธิภาพเนื่องจากทรัพยากรที่ฝังไว้ไม่สามารถนำกลับมาใช้ใหม่ได้
  • แม้ว่าคุณจะแคช HTML ได้ แต่ระบบจะไม่แชร์ทรัพยากรแบบอินไลน์ระหว่างเอกสาร ซึ่งจะลดประสิทธิภาพการแคชเมื่อเทียบกับไฟล์ภายนอกที่แคชและนำกลับมาใช้ใหม่ได้ทั่วทั้งต้นทาง
  • หากคุณแทรกในบรรทัดมากเกินไป จะทำให้เครื่องสแกนการโหลดล่วงหน้าค้นพบทรัพยากรในภายหลังในเอกสารได้ช้าลง เนื่องจากต้องใช้เวลานานขึ้นในการดาวน์โหลดเนื้อหาเพิ่มเติมที่แทรกในบรรทัด

ลองดูหน้านี้เป็นตัวอย่าง ในบางกรณี องค์ประกอบที่อาจเป็น LCP คือรูปภาพที่ด้านบนของหน้า และ CSS อยู่ในไฟล์แยกต่างหากซึ่งโหลดโดยองค์ประกอบ <link> หน้านี้ยังใช้แบบอักษรบนเว็บ 4 แบบซึ่งขอเป็นไฟล์แยกจากทรัพยากร CSS

แผนภูมิลำดับการทำงานของเครือข่าย WebPageTest ของหน้าเว็บที่มีไฟล์ CSS ภายนอกซึ่งมีการอ้างอิงแบบอักษร 4 แบบ เครื่องสแกนการโหลดล่วงหน้าจะค้นพบรูปภาพที่อาจเป็น LCP ในเวลาที่เหมาะสม
รูปที่ 12: แผนภูมิ Waterfall ของเครือข่าย WebPageTest ของหน้าเว็บที่เรียกใช้ใน Chrome บนอุปกรณ์เคลื่อนที่ผ่านการเชื่อมต่อ 3G จำลอง แคนดิเดต LCP ของหน้าเว็บคือรูปภาพที่โหลดจากองค์ประกอบ <img> แต่เครื่องสแกนการโหลดล่วงหน้าค้นพบเนื่องจาก CSS และแบบอักษรที่จำเป็นสำหรับการโหลดหน้าเว็บอยู่ในทรัพยากรแยกต่างหาก ซึ่งไม่ทำให้เครื่องสแกนการโหลดล่วงหน้าทำงานช้าลง

แล้วจะเกิดอะไรขึ้นหาก CSS และแบบอักษรทั้งหมดอยู่ในทรัพยากร Base64

แผนภูมิลำดับการทำงานของเครือข่าย WebPageTest ของหน้าเว็บที่มีไฟล์ CSS ภายนอกซึ่งมีการอ้างอิงแบบอักษร 4 แบบ เครื่องสแกนการโหลดล่วงหน้าล่าช้าอย่างมากจากการค้นพบรูปภาพ LCP
รูปที่ 13: แผนภูมิน้ำตกของเครือข่าย WebPageTest ของหน้าเว็บที่เรียกใช้ใน Chrome บนอุปกรณ์เคลื่อนที่ผ่านการเชื่อมต่อ 3G จำลอง LCP ที่มีสิทธิ์ของหน้าเว็บคือรูปภาพที่โหลดจากองค์ประกอบ <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 ส่งผลต่อความเร็วของหน้าเว็บอย่างแน่นอน นักพัฒนาแอปไม่เพียงแต่ต้องพึ่งพา WebView เพื่อมอบการโต้ตอบ แต่ยังต้องพึ่งพา WebView เพื่อส่งมอบเนื้อหาด้วย ซึ่งจะช่วยปรับปรุงประสบการณ์ของนักพัฒนาแอปในบางด้าน แต่ประโยชน์สำหรับนักพัฒนาแอปไม่ได้หมายความว่าผู้ใช้จะได้รับประโยชน์เสมอไป

รูปแบบหนึ่งที่ทำให้ตัวสแกนการโหลดล่วงหน้าไม่ทำงานคือการแสดงผลมาร์กอัปด้วย JavaScript ฝั่งไคลเอ็นต์

วอเตอร์ฟอลล์เครือข่าย WebPageTest แสดงหน้าพื้นฐานที่มีรูปภาพและข้อความที่แสดงผลอย่างสมบูรณ์ในไคลเอ็นต์ใน JavaScript เนื่องจากมาร์กอัปอยู่ใน JavaScript สแกนเนอร์การโหลดล่วงหน้าจึงตรวจหาทรัพยากรใดๆ ไม่ได้ นอกจากนี้ ทรัพยากรทั้งหมดจะล่าช้าเนื่องจากเวลาในการประมวลผลและเครือข่ายเพิ่มเติมที่เฟรมเวิร์ก JavaScript ต้องการ
รูปที่ 14: แผนภูมิลำดับขั้นของเครือข่าย WebPageTest ของหน้าเว็บที่ฝั่งไคลเอ็นต์แสดงผลซึ่งทำงานใน Chrome บนอุปกรณ์เคลื่อนที่ผ่านการเชื่อมต่อ 3G จำลอง เนื่องจากเนื้อหาอยู่ใน JavaScript และต้องอาศัยเฟรมเวิร์กในการแสดงผล เครื่องสแกนการโหลดล่วงหน้าจึงซ่อนทรัพยากรรูปภาพในการมาร์กอัปที่ฝั่งไคลเอ็นต์แสดงผล รูปที่ 9 แสดงประสบการณ์การใช้งานที่เทียบเท่าซึ่งแสดงผลฝั่งเซิร์ฟเวอร์

เมื่อเพย์โหลดมาร์กอัปอยู่ใน JavaScript และ JavaScript เป็นผู้แสดงผลทั้งหมดในเบราว์เซอร์ ทรัพยากรใดๆ ในมาร์กอัปนั้นจะมองไม่เห็นสำหรับเครื่องสแกนการโหลดล่วงหน้า ซึ่งจะทำให้การค้นพบทรัพยากรที่สำคัญล่าช้า และส่งผลต่อ LCP อย่างแน่นอน ในกรณีของตัวอย่างเหล่านี้ คำขอรูปภาพ LCP จะล่าช้าอย่างมากเมื่อเทียบกับประสบการณ์การใช้งานที่เทียบเท่าซึ่งแสดงผลฝั่งเซิร์ฟเวอร์ที่ไม่ต้องใช้ JavaScript ในการแสดง

แม้จะเบี่ยงเบนไปจากหัวข้อหลักของบทความนี้เล็กน้อย แต่ผลของการแสดงผลมาร์กอัปในไคลเอ็นต์นั้นมีมากกว่าการหลีกเลี่ยงเครื่องสแกนการโหลดล่วงหน้า ประการแรก การใช้ JavaScript เพื่อเพิ่มประสิทธิภาพประสบการณ์การใช้งานที่ไม่จำเป็นต้องใช้ JavaScript จะทำให้เกิดเวลาในการประมวลผลที่ไม่จำเป็น ซึ่งอาจส่งผลต่อ Interaction to Next Paint (INP) การแสดงผลมาร์กอัปจำนวนมากในไคลเอ็นต์มีแนวโน้มที่จะสร้างงานที่ใช้เวลานานมากกว่าการที่เซิร์ฟเวอร์ส่งมาร์กอัปจำนวนเท่ากัน สาเหตุของเรื่องนี้ (นอกเหนือจากการประมวลผลเพิ่มเติมที่ JavaScript เกี่ยวข้อง) คือเบราว์เซอร์จะสตรีมมาร์กอัปจากเซิร์ฟเวอร์และแบ่งการแสดงผลออกเป็นส่วนๆ ในลักษณะที่มักจะจำกัดงานที่ใช้เวลานาน ในทางกลับกัน มาร์กอัปที่แสดงผลฝั่งไคลเอ็นต์จะได้รับการจัดการเป็นงานแบบโมโนลิธงานเดียว ซึ่งอาจส่งผลต่อ INP ของหน้าเว็บ

วิธีแก้ไขในสถานการณ์นี้ขึ้นอยู่กับคำตอบของคำถามที่ว่า มีเหตุผลใดที่เซิร์ฟเวอร์ไม่สามารถระบุมาร์กอัปของหน้าเว็บได้แทนที่จะแสดงผลในไคลเอ็นต์ หากคำตอบคือ "ไม่" คุณควรพิจารณาการแสดงผลฝั่งเซิร์ฟเวอร์ (SSR) หรือมาร์กอัปที่สร้างแบบคงที่หากเป็นไปได้ เนื่องจากจะช่วยให้เครื่องสแกนการโหลดล่วงหน้าค้นพบและดึงข้อมูลทรัพยากรที่สำคัญล่วงหน้าได้

หากหน้าเว็บต้องใช้ JavaScript เพื่อแนบฟังก์ชันการทํางานกับมาร์กอัปบางส่วนของหน้าเว็บ คุณก็ยังทําได้ด้วย SSR ไม่ว่าจะใช้ JavaScript แบบดั้งเดิมหรือไฮเดรชันเพื่อให้ได้ประโยชน์สูงสุดจากทั้ง 2 อย่าง

ช่วยให้เครื่องสแกนการโหลดล่วงหน้าช่วยคุณได้

เครื่องสแกนการโหลดล่วงหน้าเป็นการเพิ่มประสิทธิภาพเบราว์เซอร์ที่มีประสิทธิภาพสูง ซึ่งช่วยให้หน้าเว็บโหลดได้เร็วขึ้นในระหว่างการเริ่มต้น การหลีกเลี่ยงรูปแบบที่ทำให้ความสามารถในการค้นพบทรัพยากรที่สำคัญล่วงหน้าลดลง ไม่เพียงแต่จะทำให้การพัฒนาซอฟต์แวร์ง่ายขึ้นเท่านั้น แต่ยังสร้างประสบการณ์ของผู้ใช้ที่ดีขึ้น ซึ่งจะส่งผลให้เมตริกหลายๆ อย่างดีขึ้น รวมถึง Web Vitals บางรายการด้วย

สรุปสิ่งที่คุณควรทราบจากโพสต์นี้มีดังนี้

  • ตัวสแกนการโหลดล่วงหน้าของเบราว์เซอร์คือตัวแยกวิเคราะห์ HTML รองที่จะสแกนก่อนตัวแยกวิเคราะห์หลักหากถูกบล็อกเพื่อค้นหาทรัพยากรที่สามารถดึงข้อมูลได้เร็วขึ้น
  • เครื่องสแกนการโหลดล่วงหน้าจะค้นพบไม่ได้หากไม่มีทรัพยากรในมาร์กอัปที่เซิร์ฟเวอร์ระบุในคำขอการนำทางครั้งแรก วิธีที่สแกนเนอร์ก่อนการโหลดอาจถูกหลีกเลี่ยงได้รวมถึง (แต่ไม่จำกัดเพียง) วิธีต่อไปนี้
    • แทรกทรัพยากรลงใน DOM ด้วย JavaScript ไม่ว่าจะเป็นสคริปต์ รูปภาพ สไตล์ชีต หรือสิ่งอื่นๆ ที่ควรอยู่ในเพย์โหลดมาร์กอัปเริ่มต้นจากเซิร์ฟเวอร์
    • การโหลดแบบ Lazy Loading รูปภาพหรือ iframe เหนือพับหน้าเว็บแบบเลื่อนโดยใช้โซลูชัน JavaScript
    • การแสดงผลมาร์กอัปในไคลเอ็นต์ซึ่งอาจมีการอ้างอิงถึงทรัพยากรย่อยของเอกสารโดยใช้ JavaScript
  • ตัวสแกนการโหลดล่วงหน้าจะสแกนเฉพาะ HTML แต่จะไม่ตรวจสอบเนื้อหาของทรัพยากรอื่นๆ โดยเฉพาะ CSS ซึ่งอาจมีการอ้างอิงถึงชิ้นงานที่สำคัญ รวมถึงชิ้นงานที่อาจเป็น LCP

หากหลีกเลี่ยงรูปแบบที่ส่งผลเสียต่อความสามารถของเครื่องสแกนการโหลดล่วงหน้าในการเพิ่มประสิทธิภาพการโหลดไม่ได้ ไม่ว่าจะด้วยเหตุผลใดก็ตาม ให้พิจารณาคำใบ้ทรัพยากร rel=preload หากใช้ rel=preload ให้ทดสอบในเครื่องมือในห้องทดลองเพื่อให้แน่ใจว่าคุณได้รับเอฟเฟกต์ที่ต้องการ สุดท้ายนี้ อย่าโหลดทรัพยากรล่วงหน้ามากเกินไป เพราะเมื่อจัดลำดับความสำคัญทุกอย่าง ก็จะไม่มีอะไรที่สำคัญเลย

แหล่งข้อมูล

รูปภาพหลักจาก Unsplash โดย Mohammad Rahmani