บทนำ
ในบทความนี้ เราจะสอนวิธีโหลด 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 และ Bootstrapping เร็วขึ้นได้…
HTML5 เข้ามาช่วยแก้ปัญหา
<script src="//other-domain.com/1.js" async></script>
<script src="2.js" async></script>
HTML5 มีแอตทริบิวต์ใหม่ "async" ซึ่งจะถือว่าคุณจะไม่ได้ใช้ document.write
แต่จะไม่รอจนกว่าเอกสารจะได้รับการแยกวิเคราะห์เพื่อดำเนินการ เบราว์เซอร์จะดาวน์โหลดสคริปต์ทั้ง 2 รายการพร้อมกันและเรียกใช้สคริปต์โดยเร็วที่สุด
ขออภัย เนื่องจากโค้ดจะทํางานโดยเร็วที่สุด "2.js" อาจทํางานก่อน "1.js" ซึ่งไม่เป็นปัญหาหากโค้ดดังกล่าวทำงานแยกกัน เช่น "1.js" เป็นสคริปต์การติดตามที่ไม่มีส่วนเกี่ยวข้องกับ "2.js" แต่หาก "1.js" เป็นสำเนา CDN ของ jQuery ที่ "2.js" ต้องใช้ หน้าเว็บของคุณจะเต็มไปด้วยข้อผิดพลาด เหมือนกับระเบิดลูกระเบิดใน… ฉันไม่รู้เหมือนกัน
เรารู้แล้วว่าต้องใช้อะไร นั่นคือไลบรารี JavaScript
เป้าหมายสูงสุดคือการดาวน์โหลดชุดสคริปต์ทันทีโดยไม่บล็อกการแสดงผลและดำเนินการโดยเร็วที่สุดตามลำดับที่เพิ่ม ขออภัย HTML ไม่ชอบคุณและไม่อนุญาตให้คุณทำเช่นนั้น
ปัญหานี้ได้รับการแก้ไขด้วย JavaScript ในบางรูปแบบ บางรายการกำหนดให้คุณทำการเปลี่ยนแปลง JavaScript โดยรวมไว้ในการเรียกกลับที่ไลบรารีเรียกตามลำดับที่ถูกต้อง (เช่น RequireJS) ส่วนคนอื่นๆ จะใช้ XHR เพื่อดาวน์โหลดแบบขนานกัน จากนั้นจึงดาวน์โหลด eval()
ตามลําดับที่ถูกต้อง ซึ่งใช้ไม่ได้กับสคริปต์ในโดเมนอื่น เว้นแต่จะมีส่วนหัว CORS และเบราว์เซอร์รองรับ บางคนยังใช้แฮ็กสุดเจ๋งอย่าง LabJS ด้วย
การแฮ็กดังกล่าวเกี่ยวข้องกับการหลอกให้เบราว์เซอร์ดาวน์โหลดทรัพยากรในลักษณะที่จะทริกเกอร์เหตุการณ์เมื่อดาวน์โหลดเสร็จ แต่หลีกเลี่ยงการดำเนินการดังกล่าว ใน LabJS ระบบจะเพิ่มสคริปต์ด้วยประเภท MIME ที่ไม่ถูกต้อง เช่น <script type="script/cache" src="...">
เมื่อดาวน์โหลดสคริปต์ทั้งหมดแล้ว ระบบจะเพิ่มสคริปต์เหล่านั้นอีกครั้งด้วยประเภทที่ถูกต้อง โดยหวังว่าเบราว์เซอร์จะดึงสคริปต์เหล่านั้นจากแคชโดยตรงและดำเนินการตามลำดับโดยทันที ซึ่งขึ้นอยู่กับลักษณะการทำงานที่สะดวกแต่ไม่ได้ระบุไว้ และใช้งานไม่ได้เมื่อ HTML5 ประกาศว่าเบราว์เซอร์ไม่ควรดาวน์โหลดสคริปต์ที่มีประเภทที่ไม่รู้จัก โปรดทราบว่า LabJS ปรับตัวตามการเปลี่ยนแปลงเหล่านี้และตอนนี้ใช้วิธีการต่างๆ ในบทความนี้ร่วมกัน
อย่างไรก็ตาม ตัวโหลดสคริปต์ก็มีปัญหาด้านประสิทธิภาพเช่นกัน คุณจะต้องรอให้ JavaScript ของไลบรารีดาวน์โหลดและแยกวิเคราะห์ก่อน สคริปต์ใดก็ตามที่จัดการอยู่จึงจะเริ่มดาวน์โหลดได้ นอกจากนี้ เราจะโหลดโปรแกรมโหลดสคริปต์อย่างไร เราจะโหลดสคริปต์ที่บอกโปรแกรมโหลดสคริปต์ว่าจะโหลดอะไรได้อย่างไร ใครเป็นผู้เฝ้าดูผู้เฝ้าดู Why am I naked? คำถามเหล่านี้ล้วนแต่เป็นคำถามที่ยาก
โดยทั่วไปแล้ว หากคุณต้องดาวน์โหลดไฟล์สคริปต์เพิ่มเติมก่อนที่จะดาวน์โหลดสคริปต์อื่นๆ แสดงว่าคุณแพ้การต่อสู้ด้านประสิทธิภาพแล้ว
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 ธรรมดาทำไม่ได้ การที่ระบุอย่างชัดเจนว่าไม่ใช่แบบ async จะเป็นการเพิ่มสคริปต์ลงในคิวการดำเนินการ ซึ่งเป็นคิวเดียวกับที่เพิ่มสคริปต์ในตัวอย่าง HTML ธรรมดารายการแรก อย่างไรก็ตาม การสร้างแบบไดนามิกจะทำให้สคริปต์ทำงานนอกการแยกวิเคราะห์เอกสาร ดังนั้นจึงไม่มีการบล็อกการแสดงผลขณะที่ดาวน์โหลด (อย่าสับสนระหว่างการโหลดสคริปต์แบบไม่ใช้ Async กับ XHR แบบซิงค์ ซึ่งเป็นสิ่งที่ไม่ควรทำ)
สคริปต์ด้านบนควรรวมไว้กับส่วนหัวของหน้าเว็บ โดยจัดคิวการดาวน์โหลดสคริปต์โดยเร็วที่สุดโดยไม่รบกวนการแสดงผลแบบเป็นขั้นเป็นตอน และดำเนินการโดยเร็วที่สุดตามลำดับที่คุณระบุ คุณดาวน์โหลด "2.js" ก่อนที่จะดาวน์โหลด "1.js" ได้โดยไม่เสียค่าใช้จ่าย แต่ระบบจะไม่เรียกใช้ "2.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 ครั้ง โดย 1 ครั้งผ่านองค์ประกอบลิงก์ และอีก 1 ครั้งในสคริปต์
การแก้ไข: ก่อนหน้านี้เราระบุว่าข้อมูลเหล่านี้ได้รับการสแกนโดยเครื่องมือสแกนการโหลดล่วงหน้า แต่ไม่ใช่ ข้อมูลเหล่านี้ได้รับการสแกนโดยโปรแกรมแยกวิเคราะห์ทั่วไป อย่างไรก็ตาม ตัวสแกนการโหลดล่วงหน้าอาจตรวจพบรายการเหล่านี้ได้ แต่ยังไม่ได้ดำเนินการ ส่วนสคริปต์ที่รวมอยู่ในโค้ดที่เรียกใช้งานได้จะโหลดล่วงหน้าไม่ได้ ขอขอบคุณ 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
ในบรรทัดแรกส่วนหัวของเอกสาร ซึ่งจะเปลี่ยนไปใช้การโหลดสคริปต์ตามสถานะ "พร้อมใช้งาน" ของ 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 กล่าวว่า: ฉันไม่ทราบว่า "async" คืออะไร แต่ฉันจะเรียกใช้สคริปต์ที่เพิ่มผ่าน JS ตามลำดับที่เพิ่ม Safari 5.0 บอกว่า: เราเข้าใจ "async" แต่ไม่เข้าใจการตั้งค่าเป็น "false" ด้วย JS เราจะเรียกใช้สคริปต์ทันทีที่ผู้ใช้เข้าชม ไม่ว่าจะเข้าชมในลำดับใดก็ตาม IE < 10 แสดงข้อความว่า: ไม่เข้าใจ "async" แต่มีวิธีแก้ปัญหาโดยใช้ "onreadystatechange" เบราว์เซอร์อื่นๆ ในสีแดงแสดงข้อความว่า: ฉันไม่เข้าใจ "async" นี้ ฉันจะเรียกใช้สคริปต์ทันทีที่โหลด ไม่ว่าจะโหลดในลำดับใดก็ตาม ส่วนที่เหลือหมายความว่า เราเป็นเพื่อนกัน เราจะทำตามขั้นตอนที่ถูกต้อง