บทนำ
ในบทความนี้ เราจะสอนวิธีโหลด JavaScript บางส่วนในเบราว์เซอร์และเรียกใช้
เดี๋ยวก่อน กลับมา ฉันรู้ว่ามันฟังดูธรรมดาและธรรมดา แต่อย่าลืมว่าสิ่งนี้เกิดขึ้นในเบราว์เซอร์ซึ่งตามหลักทฤษฎีแล้วกลายเป็นหลุมพรางตัวเดิม การทราบข้อบกพร่องเหล่านี้จะช่วยให้คุณเลือกวิธีโหลดสคริปต์ที่เร็วที่สุดและรบกวนน้อยที่สุดได้ หากมีเวลาไม่มากนัก ให้ข้ามไปที่ข้อมูลอ้างอิงฉบับย่อ
สำหรับผู้เริ่มต้น ต่อไปนี้คือวิธีที่ข้อกำหนดกำหนดวิธีการต่างๆ ที่สคริปต์จะดาวน์โหลดและเรียกใช้ได้
เช่นเดียวกับข้อกำหนดทั้งหมดของ WHATWG ตอนแรกเอกสารนี้ดูเหมือนซากปรักหักพังจากระเบิดลูกระเบิดหลายลูกในโรงงานสcrabble แต่เมื่ออ่านเป็นครั้งที่ 5 และเช็ดเลือดออกจากดวงตาแล้ว เอกสารนี้กลับน่าสนใจทีเดียว
สคริปต์แรกของฉันประกอบด้วย
<script src="//other-domain.com/1.js"></script>
<script src="2.js"></script>
ว้าว ความเรียบง่ายที่แสนสุข ในกรณีนี้ เบราว์เซอร์จะดาวน์โหลดสคริปต์ทั้ง 2 รายการพร้อมกันและเรียกใช้สคริปต์โดยเร็วที่สุดโดยคงลำดับไว้ "2.js" จะไม่ทํางานจนกว่า "1.js" จะทํางาน (หรือทํางานไม่สําเร็จ) "1.js" จะไม่ทํางานจนกว่าสคริปต์หรือสไตล์ชีตก่อนหน้าจะทํางาน เป็นต้น
แต่เบราว์เซอร์จะบล็อกการแสดงผลหน้าเว็บเพิ่มเติมขณะที่เหตุการณ์ทั้งหมดนี้เกิดขึ้น สาเหตุคือ DOM API จาก "ยุคแรกๆ ของเว็บ" ที่อนุญาตให้เพิ่มสตริงต่อท้ายเนื้อหาที่โปรแกรมแยกวิเคราะห์กำลังประมวลผล เช่น document.write
เบราว์เซอร์รุ่นใหม่จะยังคงสแกนหรือแยกวิเคราะห์เอกสารในเบื้องหลังและเรียกให้ดาวน์โหลดเนื้อหาภายนอกที่อาจต้องใช้ (js, รูปภาพ, css ฯลฯ) แต่ยังคงบล็อกการแสดงผล
ด้วยเหตุนี้ ผู้เชี่ยวชาญด้านประสิทธิภาพจึงแนะนำให้ใส่องค์ประกอบสคริปต์ไว้ที่ท้ายเอกสาร เนื่องจากจะบล็อกเนื้อหาให้น้อยที่สุด นั่นหมายความว่าเบราว์เซอร์จะมองไม่เห็นสคริปต์ของคุณจนกว่าจะดาวน์โหลด HTML ทั้งหมด และเมื่อถึงจุดนั้นเอง เบราว์เซอร์ก็เริ่มดาวน์โหลดเนื้อหาอื่น เช่น CSS, รูปภาพ และ iframe เบราว์เซอร์สมัยใหม่ฉลาดพอที่จะให้ความสำคัญกับ JavaScript มากกว่าภาพ แต่เราทำได้ดีกว่านี้
ขอขอบคุณ IE (เราไม่ได้พูดจาประชดประชัน)
<script src="//other-domain.com/1.js" defer></script>
<script src="2.js" defer></script>
Microsoft ตระหนักถึงปัญหาด้านประสิทธิภาพเหล่านี้และได้เปิดตัว "เลื่อน" ใน Internet Explorer 4 ข้อความนี้หมายความว่า "ฉันสัญญาว่าจะไม่แทรกข้อมูลลงในโปรแกรมแยกวิเคราะห์โดยใช้สิ่งต่างๆ เช่น document.write
หากฉันผิดสัญญา คุณมีสิทธิ์ลงโทษฉันด้วยวิธีใดก็ได้ที่เห็นว่าเหมาะสม" แอตทริบิวต์นี้ได้รวมอยู่ใน HTML4 และปรากฏในเบราว์เซอร์อื่นๆ
ในตัวอย่างนี้ เบราว์เซอร์จะดาวน์โหลดสคริปต์ทั้ง 2 รายการพร้อมกันและดำเนินการก่อนที่จะเรียกใช้ DOMContentLoaded
โดยรักษาลําดับไว้
"เลื่อน" กลายเป็นเรื่องยุ่งเหยิงราวกับระเบิดลูกระเบิดในโรงงานเลี้ยงแกะ เรามีรูปแบบการเพิ่มสคริปต์ 6 รูปแบบ ได้แก่ แอตทริบิวต์ "src" และ "defer" รวมถึงแท็กสคริปต์กับสคริปต์ที่เพิ่มแบบไดนามิก แน่นอนว่าเบราว์เซอร์ต่างๆ ไม่ตรงกันกับลำดับที่ควรเรียกใช้ Mozilla เขียนบทความที่ยอดเยี่ยมเกี่ยวกับปัญหานี้ได้จากปี 2009
WHATWG ระบุลักษณะการทํางานอย่างชัดเจน โดยประกาศว่า "defer" ไม่มีผลกับสคริปต์ที่เพิ่มแบบไดนามิกหรือไม่มี "src" มิเช่นนั้น สคริปต์ที่เลื่อนไว้ควรทํางานหลังจากแยกวิเคราะห์เอกสารแล้ว ตามลําดับที่เพิ่ม
ขอบคุณนะ IE! (โอเค เราพูดเล่นนะ)
ให้แล้วก็ผลแพ้ ขออภัย เกิดข้อบกพร่องร้ายแรงใน IE4-9 ซึ่งอาจทําให้สคริปต์ทํางานในลําดับที่ไม่คาดคิด สิ่งที่เกิดขึ้นมีดังนี้
1.js
console.log('1');
document.getElementsByTagName('p')[0].innerHTML = 'Changing some content';
console.log('2');
2.js
console.log('3');
สมมติว่าหน้าเว็บมีย่อหน้า ลําดับที่คาดไว้ของบันทึกคือ [1, 2, 3] แต่คุณจะเห็น [1, 3, 2] ใน IE9 และเวอร์ชันที่ต่ำกว่า การดำเนินการ DOM บางรายการทําให้ IE หยุดการเรียกใช้สคริปต์ปัจจุบันชั่วคราวและเรียกใช้สคริปต์อื่นๆ ที่รอดําเนินการก่อนดำเนินการต่อ
อย่างไรก็ตาม แม้ในการใช้งานที่ไม่มีข้อบกพร่อง เช่น IE10 และเบราว์เซอร์อื่นๆ การดำเนินการของสคริปต์จะล่าช้าจนกว่าระบบจะดาวน์โหลดและแยกวิเคราะห์เอกสารทั้งฉบับ วิธีนี้อาจสะดวกในกรณีที่คุณจะต้องรอ DOMContentLoaded
อยู่แล้ว แต่หากต้องการเพิ่มประสิทธิภาพอย่างจริงจัง คุณก็เริ่มเพิ่ม Listeners และ Bootstrap เร็วขึ้นได้…
HTML5 เข้ามาช่วยแก้ปัญหา
<script src="//other-domain.com/1.js" async></script>
<script src="2.js" async></script>
HTML5 ให้แอตทริบิวต์ใหม่คือ "async" ซึ่งจะถือว่าคุณไม่ได้ใช้ document.write
แต่ไม่รอจนกว่าจะมีการแยกวิเคราะห์เอกสาร เบราว์เซอร์จะดาวน์โหลดสคริปต์ทั้งสองพร้อมกันและเรียกใช้โดยเร็วที่สุด
น่าเสียดายที่ “2.js” อาจทำงานก่อน “1.js” นี้ก็ไม่เป็นไรหากแยกเป็นอิสระจากกัน อาจเป็น “1.js” เป็นสคริปต์ติดตามที่ไม่มีส่วนเกี่ยวข้องกับ “2.js” แต่หาก “1.js” เป็นสำเนา CDN ของ jQuery ที่ “2.js” ต้องใช้ หน้าเว็บของคุณก็จะไม่มีข้อผิดพลาด I-bomb นี้...
เรารู้แล้วว่าต้องใช้อะไร นั่นคือไลบรารี JavaScript
สิ่งที่จะเกิดขึ้นก็คือการดาวน์โหลดชุดสคริปต์นั้นทันทีโดยไม่มีการบล็อกการแสดงผล และทำงานโดยเร็วที่สุดตามลำดับที่เพิ่มเข้ามา ขออภัย HTML ไม่ชอบคุณและจะไม่อนุญาตให้คุณทำเช่นนั้น
ปัญหานี้ได้รับการแก้ไขด้วย JavaScript ในบางรูปแบบ บางรายการกำหนดให้คุณทำการเปลี่ยนแปลง JavaScript โดยรวมไว้ในการเรียกกลับที่ไลบรารีเรียกตามลำดับที่ถูกต้อง (เช่น RequireJS) แต่บางโปรแกรมจะใช้ XHR เพื่อดาวน์โหลดพร้อมกัน จากนั้นใช้ eval()
ในลำดับที่ถูกต้อง ซึ่งใช้ไม่ได้กับสคริปต์ในโดเมนอื่นเว้นแต่ว่าจะมีส่วนหัว CORS และเบราว์เซอร์รองรับ บางคนยังใช้แฮ็กสุดเจ๋งอย่าง LabJS ด้วย
การแฮ็กดังกล่าวเกี่ยวข้องกับการหลอกให้เบราว์เซอร์ดาวน์โหลดทรัพยากรในลักษณะที่จะทริกเกอร์เหตุการณ์เมื่อเสร็จสมบูรณ์ แต่หลีกเลี่ยงการดำเนินการ ใน LabJS ระบบจะเพิ่มสคริปต์ด้วยประเภท MIME ที่ไม่ถูกต้อง เช่น <script type="script/cache" src="...">
เมื่อดาวน์โหลดสคริปต์ทั้งหมดแล้ว ระบบจะเพิ่มสคริปต์เหล่านั้นอีกครั้งด้วยประเภทที่ถูกต้อง โดยหวังว่าเบราว์เซอร์จะดึงสคริปต์เหล่านั้นจากแคชโดยตรงและดำเนินการตามลำดับโดยทันที ซึ่งขึ้นอยู่กับลักษณะการทำงานที่สะดวกแต่ไม่ได้ระบุไว้ และใช้งานไม่ได้เมื่อ HTML5 ประกาศว่าเบราว์เซอร์ไม่ควรดาวน์โหลดสคริปต์ที่มีประเภทที่ไม่รู้จัก โปรดทราบว่า LabJS ปรับตัวตามการเปลี่ยนแปลงเหล่านี้และตอนนี้ใช้วิธีการต่างๆ ในบทความนี้ร่วมกัน
อย่างไรก็ตาม ตัวโหลดสคริปต์ก็มีปัญหาด้านประสิทธิภาพเช่นกัน คุณจะต้องรอให้ JavaScript ของไลบรารีดาวน์โหลดและแยกวิเคราะห์ก่อน สคริปต์ใดก็ตามที่จัดการจึงจะเริ่มดาวน์โหลดได้ นอกจากนี้ เราจะโหลดโปรแกรมโหลดสคริปต์อย่างไร เราจะโหลดสคริปต์ที่บอกโปรแกรมโหลดสคริปต์ว่าจะโหลดอะไรได้อย่างไร ใครเป็นผู้เฝ้าดูผู้เฝ้าดู Why am I naked? คำถามเหล่านี้ล้วนแต่เป็นคำถามที่ยาก
พูดง่ายๆ ก็คือ ถ้าคุณต้องดาวน์โหลดไฟล์สคริปต์เพิ่มอีก 1 ไฟล์ก่อนที่จะคิดดาวน์โหลดสคริปต์อื่นๆ คุณแพ้การแข่งขันด้านประสิทธิภาพตรงนั้นเลย
DOM เข้ามาช่วย
คําตอบอยู่ในข้อกําหนดของ HTML5 แม้ว่าจะซ่อนอยู่ด้านล่างของส่วนการโหลดสคริปต์
เรามาแปลความหมายของ "Earthling" กัน
[
'//other-domain.com/1.js',
'2.js'
].forEach(function(src) {
var script = document.createElement('script');
script.src = src;
document.head.appendChild(script);
});
สคริปต์ที่สร้างแบบไดนามิกและเพิ่มลงในเอกสารจะเป็นแบบแอซิงค์โดยค่าเริ่มต้น จะไม่บล็อกการแสดงผลและเรียกใช้ทันทีที่ดาวน์โหลด ซึ่งหมายความว่าอาจแสดงในลำดับที่ไม่ถูกต้อง อย่างไรก็ตาม เราสามารถทำเครื่องหมายอย่างชัดแจ้งว่าไม่ใช่อะซิงโครนัสได้ ดังนี้
[
'//other-domain.com/1.js',
'2.js'
].forEach(function(src) {
var script = document.createElement('script');
script.src = src;
script.async = false;
document.head.appendChild(script);
});
ซึ่งทำให้สคริปต์ของเรามีการทำงานแบบผสมผสานที่ HTML ธรรมดาทำไม่ได้ เมื่อทำงานไม่พร้อมกันอย่างชัดแจ้ง ระบบจะเพิ่มสคริปต์ลงในคิวการดำเนินการ ซึ่งเป็นคิวเดียวกับที่เพิ่มในตัวอย่าง HTML ธรรมดารายการแรกของเรา อย่างไรก็ตาม การสร้างแบบไดนามิกจะทำให้สคริปต์ทำงานนอกการแยกวิเคราะห์เอกสาร ดังนั้นจึงไม่มีการบล็อกการแสดงผลขณะที่ดาวน์โหลด (อย่าสับสนระหว่างการโหลดสคริปต์แบบไม่ใช้ Async กับ XHR แบบซิงค์ ซึ่งเป็นสิ่งที่ไม่ควรทำ)
สคริปต์ด้านบนควรรวมไว้กับส่วนหัวของหน้าเว็บ โดยจัดคิวการดาวน์โหลดสคริปต์โดยเร็วที่สุดโดยไม่รบกวนการแสดงผลแบบเป็นขั้นเป็นตอน และดำเนินการโดยเร็วที่สุดตามลำดับที่คุณระบุ “2.js” สามารถดาวน์โหลดได้ฟรีก่อน “1.js” แต่จะไม่ทำงานจนกว่า “1.js” จะดาวน์โหลดและเรียกใช้สำเร็จ หรือไม่ดำเนินการอย่างใดอย่างหนึ่ง ฮึ่มม! ดาวน์โหลดแบบไม่พร้อมกันแต่สั่งการได้!
ทุกเบราว์เซอร์ที่รองรับแอตทริบิวต์ async รองรับการโหลดสคริปต์ด้วยวิธีนี้ ยกเว้น Safari 5.0 (5.1 ใช้ได้) นอกจากนี้ ระบบยังรองรับ Firefox และ Opera ทุกเวอร์ชัน เนื่องจากเวอร์ชันที่ไม่รองรับแอตทริบิวต์ async จะเรียกใช้สคริปต์ที่เพิ่มแบบไดนามิกตามลำดับที่เพิ่มลงในเอกสารได้อย่างสะดวก
นี่เป็นวิธีที่เร็วที่สุดในการโหลดสคริปต์ใช่ไหม ถูกต้องไหม
หากตัดสินใจแบบไดนามิกว่าจะโหลดสคริปต์ใด ก็ใช่ แต่หากไม่ ก็อาจไม่ใช่ ในตัวอย่างนี้เบราว์เซอร์ต้องแยกวิเคราะห์และเรียกใช้สคริปต์เพื่อค้นหาว่าควรดาวน์โหลดสคริปต์ใด ซึ่งจะซ่อนสคริปต์ของคุณจากโปรแกรมสแกนการโหลดล่วงหน้า เบราว์เซอร์ใช้เครื่องมือสแกนเหล่านี้เพื่อค้นหาทรัพยากรในหน้าเว็บที่คุณมีแนวโน้มจะเข้าชมในลำดับถัดไป หรือค้นหาทรัพยากรของหน้าเว็บขณะที่เครื่องมือแยกวิเคราะห์ถูกบล็อกโดยทรัพยากรอื่น
เราสามารถเพิ่มการค้นพบได้ด้วยการใช้ข้อความนี้ในส่วนหัวของเอกสาร
<link rel="subresource" href="//other-domain.com/1.js">
<link rel="subresource" href="2.js">
ซึ่งบอกให้เบราว์เซอร์ทราบว่าหน้าเว็บต้องใช้ 1.js และ 2.js link[rel=subresource]
คล้ายกับ link[rel=prefetch]
แต่มีความหมายต่างกัน ขออภัย ปัจจุบันเครื่องมือนี้สนับสนุนเฉพาะใน Chrome และคุณต้องประกาศว่าสคริปต์ใดที่จะโหลด 2 ครั้ง ครั้งแรกผ่านองค์ประกอบของลิงก์ และอีกครั้งในสคริปต์ของคุณ
การแก้ไข: ก่อนหน้านี้เราระบุว่าข้อมูลเหล่านี้ได้รับการสแกนโดยเครื่องมือสแกนการโหลดล่วงหน้า แต่ไม่ใช่ ข้อมูลเหล่านี้ได้รับการสแกนโดยโปรแกรมแยกวิเคราะห์ทั่วไป อย่างไรก็ตาม ตัวสแกนการโหลดล่วงหน้าอาจตรวจพบรายการเหล่านี้ได้ แต่ยังไม่ได้ดำเนินการ ส่วนสคริปต์ที่รวมอยู่ในโค้ดที่เรียกใช้งานได้จะโหลดล่วงหน้าไม่ได้ ขอขอบคุณ Yoav Weiss ที่แก้ไขให้ในความคิดเห็น
ฉันคิดว่าบทความนี้น่าหวาดหวั่น
สถานการณ์กำลังซึมเศร้าและคุณน่าจะรู้สึกซึมเศร้า ไม่มีวิธีใดที่ใช้ซ้ำๆ ได้แต่ใช้ประกาศไม่ได้ ในการดาวน์โหลดสคริปต์อย่างรวดเร็วและไม่พร้อมกันในขณะที่ควบคุมลำดับการดำเนินการ เมื่อใช้ HTTP2/SPDY คุณจะลดค่าใช้จ่ายเพิ่มเติมของคำขอได้จนทำให้การส่งสคริปต์ในไฟล์ขนาดเล็กหลายไฟล์ที่แคชแยกกันได้เป็นวิธีที่เร็วที่สุด ลองจินตนาการว่า
<script src="dependencies.js"></script>
<script src="enhancement-1.js"></script>
<script src="enhancement-2.js"></script>
<script src="enhancement-3.js"></script>
…
<script src="enhancement-10.js"></script>
สคริปต์การเพิ่มประสิทธิภาพแต่ละรายการจะจัดการกับคอมโพเนนต์หน้าเว็บที่เฉพาะเจาะจง แต่ต้องใช้ฟังก์ชันยูทิลิตีใน dependencies.js ตามหลักการแล้วเราต้องการดาวน์โหลดแบบไม่พร้อมกันทั้งหมด แล้วเรียกใช้สคริปต์การปรับปรุงโดยเร็วที่สุด โดยเรียงลำดับอย่างไรก็ได้ แต่อยู่หลัง Dependencies.js เรียกได้ว่าเป็นการเพิ่มประสิทธิภาพแบบต่อเนื่อง น่าเสียดายที่ไม่มีวิธีประกาศให้บรรลุผลดังกล่าว เว้นแต่จะมีการแก้ไขตัวสคริปต์ให้ติดตามสถานะการโหลดของ Dependencies.js แม้แต่ async=false ก็ยังแก้ปัญหานี้ไม่ได้ เนื่องจากการดำเนินการ enhancement-10.js จะบล็อกที่ 1-9 มีเบราว์เซอร์เพียงชนิดเดียวที่สามารถทำเช่นนี้ได้โดยไม่มีการแฮ็ก...
IE มีไอเดียดีๆ
IE จะโหลดสคริปต์แตกต่างจากเบราว์เซอร์อื่นๆ
var script = document.createElement('script');
script.src = 'whatever.js';
IE จะเริ่มดาวน์โหลด "whatever.js" ในตอนนี้ แต่เบราว์เซอร์อื่นๆ จะไม่เริ่มดาวน์โหลดจนกว่าจะเพิ่มสคริปต์ลงในเอกสาร IE ยังมีเหตุการณ์ “readystatechange” และพร็อพเพอร์ตี้ “readystate” ซึ่งบอกความคืบหน้าในการโหลดให้เราทราบด้วย ซึ่งมีประโยชน์มากจริงๆ เพราะช่วยให้เราสามารถควบคุมการโหลดและเรียกใช้สคริปต์ได้อย่างอิสระ
var script = document.createElement('script');
script.onreadystatechange = function() {
if (script.readyState == 'loaded') {
// Our script has download, but hasn't executed.
// It won't execute until we do:
document.body.appendChild(script);
}
};
script.src = 'whatever.js';
เราสามารถสร้างรูปแบบการพึ่งพาที่ซับซ้อนได้โดยเลือกเวลาที่จะเพิ่มสคริปต์ลงในเอกสาร IE รองรับรูปแบบนี้มาตั้งแต่เวอร์ชัน 6 น่าสนใจทีเดียว แต่ยังคงมีปัญหาการค้นพบตัวโหลดล่วงหน้าเช่นเดียวกับ async=false
พอแล้ว ฉันควรโหลดสคริปต์อย่างไร
เข้าใจแล้ว หากต้องการโหลดสคริปต์ในลักษณะที่ไม่บล็อกการแสดงผล ไม่เกี่ยวข้องกับการซ้ำ และรองรับเบราว์เซอร์ได้อย่างยอดเยี่ยม เราขอแนะนําให้ทําดังนี้
<script src="//other-domain.com/1.js"></script>
<script src="2.js"></script>
นั่นคือ ที่ท้ายองค์ประกอบ body ใช่ การเป็นนักพัฒนาเว็บก็เหมือนกับการเป็นกษัตริย์ซิซิฟัส (บูม คะแนนฮิปสเตอร์ 100 คะแนนสำหรับการอ้างอิงตำนานกรีก ข้อจำกัดใน HTML และเบราว์เซอร์ช่วยป้องกันไม่ให้เราทำงานได้ดีขึ้นมาก
เราหวังว่าโมดูล JavaScript จะช่วยแก้ปัญหานี้ด้วยวิธีการโหลดสคริปต์แบบประกาศที่ไม่บล็อกและควบคุมลําดับการดําเนินการได้ แม้ว่าจะต้องเขียนสคริปต์เป็นโมดูลก็ตาม
อุ้ย ต้องมีวิธีอื่นที่ดีกว่านี้ไหม
เข้าใจแล้ว เคล็ดลับเพิ่มเติมคือหากต้องการเพิ่มประสิทธิภาพอย่างจริงจังและไม่สนใจความซับซ้อนและความซ้ำซาก คุณก็ใช้เคล็ดลับข้างต้นร่วมกันได้
ก่อนอื่น เราจะเพิ่มการประกาศทรัพยากรย่อยสำหรับตัวโหลดล่วงหน้า ดังนี้
<link rel="subresource" href="//other-domain.com/1.js">
<link rel="subresource" href="2.js">
จากนั้นแทรกในบรรทัดในส่วนหัวของเอกสาร เราจะโหลดสคริปต์ของเราด้วย JavaScript โดยใช้ async=false
และกลับไปใช้การโหลดสคริปต์แบบ Readystate ของ IE ที่ย้อนกลับไปถ่วงเวลา
var scripts = [
'1.js',
'2.js'
];
var src;
var script;
var pendingScripts = [];
var firstScript = document.scripts[0];
// Watch scripts load in IE
function stateChange() {
// Execute as many scripts in order as we can
var pendingScript;
while (pendingScripts[0] && pendingScripts[0].readyState == 'loaded') {
pendingScript = pendingScripts.shift();
// avoid future loading events from this script (eg, if src changes)
pendingScript.onreadystatechange = null;
// can't just appendChild, old IE bug if element isn't closed
firstScript.parentNode.insertBefore(pendingScript, firstScript);
}
}
// loop through our script urls
while (src = scripts.shift()) {
if ('async' in firstScript) { // modern browsers
script = document.createElement('script');
script.async = false;
script.src = src;
document.head.appendChild(script);
}
else if (firstScript.readyState) { // IE<10
// create a script and add it to our todo pile
script = document.createElement('script');
pendingScripts.push(script);
// listen for state changes
script.onreadystatechange = stateChange;
// must set src AFTER adding onreadystatechange listener
// else we'll miss the loaded event for cached scripts
script.src = src;
}
else { // fall back to defer
document.write('<script src="' + src + '" defer></'+'script>');
}
}
หลังจากใช้เทคนิคและการทำให้เป็นไฟล์ขนาดเล็กแล้ว ไฟล์มีความยาว 362 ไบต์ + URL ของสคริปต์
!function(e,t,r){function n(){for(;d[0]&&"loaded"==d[0][f];)c=d.shift(),c[o]=!i.parentNode.insertBefore(c,i)}for(var s,a,c,d=[],i=e.scripts[0],o="onreadystatechange",f="readyState";s=r.shift();)a=e.createElement(t),"async"in i?(a.async=!1,e.head.appendChild(a)):i[f]?(d.push(a),a[o]=n):e.write("<"+t+' src="'+s+'" defer></'+t+">"),a.src=s}(document,"script",[
"//other-domain.com/1.js",
"2.js"
])
การใช้การรวมสคริปต์แบบง่ายคุ้มค่ากับการเพิ่มจำนวนไบต์ไหม หากคุณใช้ JavaScript เพื่อโหลดสคริปต์แบบมีเงื่อนไขอยู่แล้ว เช่นเดียวกับที่ BBC ทำ คุณก็อาจได้รับประโยชน์จากการเรียกให้ดาวน์โหลดเหล่านั้นให้เร็วขึ้นด้วย หรืออาจไม่จำเป็น ให้ใช้วิธีการสิ้นสุดข้อความแบบง่าย
ว้าว ตอนนี้ฉันรู้แล้วว่าทำไมส่วนการโหลดสคริปต์ WHATWG จึงมีขนาดใหญ่มาก ฉันอยากดื่ม
ข้อมูลอ้างอิงโดยย่อ
องค์ประกอบสคริปต์ทั่วไป
<script src="//other-domain.com/1.js"></script>
<script src="2.js"></script>
ข้อกำหนดระบุว่า: ดาวน์โหลดพร้อมกัน ดำเนินการตามลำดับหลัง CSS ที่รอดำเนินการ บล็อกการแสดงผลจนกว่าจะเสร็จสมบูรณ์ เบราว์เซอร์ตอบว่า: ได้เลย
เลื่อน
<script src="//other-domain.com/1.js" defer></script>
<script src="2.js" defer></script>
ข้อกำหนดระบุว่า ดาวน์โหลดพร้อมกัน โดยดำเนินการตามลำดับก่อน DOMContentLoaded ละเว้น "defer" ในสคริปต์ที่ไม่มี "src" IE < 10 ระบุว่า: ฉันอาจเรียกใช้ 2.js ในช่วงครึ่งทางของการเรียกใช้ 1.js สนุกไหม เบราว์เซอร์สีแดงระบุว่า: ฉันไม่ทราบว่าสิ่งที่ "เลื่อนออกไป" นี้คืออะไร ฉันจะโหลดสคริปต์เสมือนว่าไม่มีอยู่ เบราว์เซอร์อื่นๆ พูดว่า: โอเค แต่ฉันอาจไม่ละเว้น "defer" ในสคริปต์ที่ไม่มี "src"
Async
<script src="//other-domain.com/1.js" async></script>
<script src="2.js" async></script>
ข้อมูลจำเพาะระบุว่า: ดาวน์โหลดพร้อมกัน เรียกใช้ตามลำดับที่ดาวน์โหลด เบราว์เซอร์สีแดงจะแสดงข้อความว่า "async" คืออะไร ฉันจะโหลดสคริปต์ราวกับว่าไม่มี เบราว์เซอร์อื่นๆ บอกให้ทำดังนี้
Async เท็จ
[
'1.js',
'2.js'
].forEach(function(src) {
var script = document.createElement('script');
script.src = src;
script.async = false;
document.head.appendChild(script);
});
ข้อกำหนดระบุว่า: ดาวน์โหลดพร้อมกัน ดำเนินการตามลำดับทันทีที่ดาวน์โหลดทั้งหมด Firefox < 3.6, Opera บอกว่า: ฉันไม่ทราบว่าสิ่งที่ "ไม่พร้อมกัน" นี้คืออะไร แต่บังเอิญฉันเรียกใช้สคริปต์ที่เพิ่มผ่าน JS ตามลำดับที่เพิ่มเข้ามา Safari 5.0 บอกว่า: เราเข้าใจ "async" แต่ไม่เข้าใจการตั้งค่าเป็น "false" ด้วย JS เราจะเรียกใช้สคริปต์ทันทีที่ผู้ใช้เข้าชม ไม่ว่าจะเข้าชมในลำดับใดก็ตาม IE < 10 แสดงข้อความว่า: ไม่เข้าใจ "async" แต่มีวิธีแก้ปัญหาโดยใช้ "onreadystatechange" เบราว์เซอร์อื่นๆ ในสีแดงแสดงข้อความว่า: ฉันไม่เข้าใจ "async" นี้ ฉันจะเรียกใช้สคริปต์ทันทีที่โหลด ไม่ว่าจะโหลดในลำดับใดก็ตาม ส่วนข้อความอื่นๆ หมายความว่า เราเป็นเพื่อนกัน เราจะทำตามขั้นตอนที่ถูกต้อง