โหลดโมดูลล่วงหน้า

Sérgio Gomes

เผยแพร่เมื่อวันที่ 23 พฤศจิกายน 2024

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

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

<link rel="preload"> เป็นวิธีขอทรัพยากรล่วงหน้าอย่างชัดแจ้งก่อนที่เบราว์เซอร์จะต้องการ

<head>
  <link rel="preload" as="style" href="critical-styles.css">
  <link rel="preload" as="font" crossorigin type="font/woff2" href="myfont.woff2">
</head>

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

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

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

สําหรับแท็ก <script> และ <link> คุณสามารถตั้งค่าโหมดข้อมูลเข้าสู่ระบบด้วยแอตทริบิวต์ crossorigin แต่ปรากฏว่า <script type="module"> ที่ไม่มีแอตทริบิวต์ crossorigin บ่งบอกถึงโหมดข้อมูลเข้าสู่ระบบของ omit ซึ่งไม่มีอยู่สำหรับ <link rel="preload"> ซึ่งหมายความว่าคุณจะต้องเปลี่ยนแอตทริบิวต์ crossorigin ทั้งใน <script> และ <link> เป็นค่าอื่น และคุณอาจเปลี่ยนแอตทริบิวต์ดังกล่าวได้ยากหากสิ่งที่พยายามจะโหลดล่วงหน้าเป็นโมดูลที่ต้องพึ่งพาโมดูลอื่นๆ

นอกจากนี้ การดึงข้อมูลไฟล์เป็นเพียงขั้นตอนแรกในการเรียกใช้โค้ดจริง ก่อนอื่นเบราว์เซอร์ต้องแยกวิเคราะห์และคอมไพล์ โดยควรดำเนินการล่วงหน้าด้วย เพื่อที่ว่าเมื่อต้องการใช้โมดูล โค้ดจะพร้อมใช้งาน อย่างไรก็ตาม V8 (เครื่องมือ JavaScript ของ Chrome) จะแยกวิเคราะห์และคอมไพล์โมดูลแตกต่างจาก JavaScript อื่นๆ <link rel="preload"> ไม่ได้ระบุวิธีใดๆ ที่บ่งบอกว่าไฟล์ที่โหลดเป็นโมดูล ดังนั้นเบราว์เซอร์จะทำได้เพียงโหลดไฟล์และใส่ไว้ในแคช เมื่อโหลดสคริปต์โดยใช้แท็ก <script type="module"> (หรือโหลดโดยโมดูลอื่น) แล้ว เบราว์เซอร์จะแยกวิเคราะห์และคอมไพล์โค้ดเป็นโมดูล JavaScript

สรุปสั้นๆ คือ ใช่ การมี link ประเภทที่เฉพาะเจาะจงสำหรับการโหลดโมดูลล่วงหน้าช่วยให้เราเขียน HTML ง่ายๆ ได้โดยไม่ต้องกังวลว่าจะใช้โหมดข้อมูลเข้าสู่ระบบใด ค่าเริ่มต้นใช้งานได้เลย

<head>
  <link rel="modulepreload" href="super-critical-stuff.mjs">
</head>
[...]
<script type="module" src="super-critical-stuff.mjs">

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

การรองรับเบราว์เซอร์

  • Chrome: 66
  • Edge: ≤79
  • Firefox: 115
  • Safari: 17.

แหล่งที่มา

แล้วทรัพยากร Dependency ของโมดูลล่ะ

คำถามนี้ดีมาก บทความนี้ไม่ได้กล่าวถึงเรื่องหนึ่งที่สำคัญมาก นั่นก็คือ การเกิดซ้ำ

ข้อกำหนด <link rel="modulepreload"> อนุญาตให้โหลดไม่เพียงโมดูลที่ขอเท่านั้น แต่ยังโหลดทั้งลําดับชั้นของข้อกําหนดที่เกี่ยวข้องทั้งหมดด้วย เบราว์เซอร์ไม่จำเป็นต้องดำเนินการนี้ แต่สามารถดำเนินการได้

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

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

<head>
  <!-- dog.js imports dog-head.js, which in turn imports
       dog-head-mouth.js, which imports dog-head-mouth-tongue.js. -->
  <link rel="modulepreload" href="dog-head-mouth-tongue.mjs">
  <link rel="modulepreload" href="dog-head-mouth.mjs">
  <link rel="modulepreload" href="dog-head.mjs">
  <link rel="modulepreload" href="dog.mjs">
</head>

การโหลดโมดูลล่วงหน้าช่วยเพิ่มประสิทธิภาพไหม

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

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