โหลดและรันคำสั่งของสคริปต์


265

มีหลายวิธีในการรวม JavaScript ไว้ในหน้า html ฉันรู้เกี่ยวกับตัวเลือกต่อไปนี้:

  • โค้ดอินไลน์หรือโหลดจาก URI ภายนอก
  • รวมอยู่ในแท็ก <head> หรือ <body> [ 1 , 2 ]
  • ไม่มีdeferหรือasyncแอตทริบิวต์ (สคริปต์ภายนอกเท่านั้น)
  • รวมอยู่ในแหล่งที่คงที่หรือเพิ่มแบบไดนามิกโดยสคริปต์อื่น (ที่แยกวิเคราะห์รัฐที่แตกต่างกันด้วยวิธีการที่แตกต่างกัน)

ไม่นับเบราว์เซอร์จาก harddisk, javascript: URIs และonEvent-attributes [ 3 ] มี 16 ทางเลือกในการเรียกใช้ JS และฉันแน่ใจว่าฉันลืมบางสิ่งบางอย่าง

ฉันไม่ได้กังวลกับการโหลดที่เร็ว (ขนาน) ฉันอยากรู้มากขึ้นเกี่ยวกับลำดับการดำเนินการ (ซึ่งอาจขึ้นอยู่กับลำดับการโหลดและลำดับเอกสาร ) มีการอ้างอิงที่ดี (cross-browser) ที่ครอบคลุมทุกกรณีหรือไม่? เช่นhttp://www.websiteoptimization.com/speed/tweak/defer/เพียงข้อตกลงกับ 6 ของพวกเขาและทดสอบเบราว์เซอร์เก่าส่วนใหญ่

เนื่องจากฉันกลัวว่าไม่มีนี่คือคำถามเฉพาะของฉัน: ฉันมีสคริปต์ส่วนหัว (ภายนอก) สำหรับการเริ่มต้นและการโหลดสคริปต์ จากนั้นฉันก็มีสคริปต์แบบอินไลน์ที่คงที่สองตัวที่ส่วนท้ายของเนื้อหา อันแรกให้ตัวโหลดสคริปต์ต่อท้ายองค์ประกอบสคริปต์อื่นแบบไดนามิก (อ้างอิง js ภายนอก) กับเนื้อหา วินาทีของสคริปต์แบบอินไลน์ที่คงที่ต้องการใช้ js จากสคริปต์ภายนอกที่เพิ่มเข้ามา มันสามารถพึ่งพาคนอื่นที่ถูกประหารชีวิตได้ไหม (และทำไม :-)?


คุณดูที่กำลังโหลดสคริปต์โดยไม่ปิดกั้นโดย Steve Souders หรือไม่? มันค่อนข้างเก่า แต่ก็ยังมีข้อมูลเชิงลึกที่มีค่าเกี่ยวกับพฤติกรรมของเบราว์เซอร์โดยใช้เทคนิคการโหลดสคริปต์เฉพาะ
Josh Habdas

คำตอบ:


331

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

สคริปต์ Async (โดยไม่คำนึงถึงวิธีที่ระบุว่าเป็น async) โหลดและรันตามลำดับที่คาดเดาไม่ได้ เบราว์เซอร์โหลดมันแบบขนานและเป็นอิสระในการรันตามลำดับที่ต้องการ

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

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

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

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

นี่คือคำพูดจากบทความนั้น:

สคริปต์ที่แทรกสคริปต์ดำเนินการแบบอะซิงโครนัสใน IE และ WebKit แต่ซิงโครไนซ์ใน Opera และ pre-4.0 Firefox

ส่วนที่เกี่ยวข้องของสเปค HTML5 (เบราว์เซอร์ที่สอดคล้องกับที่ใหม่กว่า) เป็นที่นี่ มีจำนวนมากที่เขียนเกี่ยวกับพฤติกรรม async เห็นได้ชัดว่าข้อกำหนดนี้ไม่ได้ใช้กับเบราว์เซอร์รุ่นเก่า (หรือเบราว์เซอร์ที่ไม่สอดคล้อง) ซึ่งพฤติกรรมที่คุณอาจต้องทดสอบเพื่อตรวจสอบ

คำพูดจากสเป็ค HTML5:

จากนั้นต้องปฏิบัติตามตัวเลือกแรกต่อไปนี้ที่อธิบายถึงสถานการณ์:

หากองค์ประกอบนั้นมีแอตทริบิวต์ src และองค์ประกอบนั้นมีแอตทริบิวต์ที่เลื่อนออกไปและองค์ประกอบนั้นถูกตั้งค่าสถานะเป็น "parser-insert" และองค์ประกอบนั้นไม่มีแอตทริบิวต์ async องค์ประกอบนั้นจะต้องเพิ่มไว้ที่ท้ายรายการ สคริปต์ที่จะดำเนินการเมื่อเอกสารเสร็จสิ้นการแยกวิเคราะห์ที่เชื่อมโยงกับเอกสารของตัวแยกวิเคราะห์ที่สร้างองค์ประกอบ

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

หากองค์ประกอบมีแอตทริบิวต์ src และองค์ประกอบนั้นถูกตั้งค่าสถานะเป็น "parser-insert" และองค์ประกอบนั้นไม่มีแอตทริบิวต์ async องค์ประกอบนั้นเป็นสคริปต์การบล็อกการแยกวิเคราะห์ที่ค้างอยู่ของเอกสารของโปรแกรมวิเคราะห์คำที่สร้างองค์ประกอบนั้น (สามารถมีได้เพียงหนึ่งสคริปต์ต่อเอกสารในแต่ละครั้ง)

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

หากองค์ประกอบไม่มีแอตทริบิวต์ src และองค์ประกอบถูกตั้งค่าสถานะเป็น "parser-insert" และเอกสารของตัวแยกวิเคราะห์ HTML หรือตัวแยกวิเคราะห์ HTML ที่สร้างองค์ประกอบสคริปต์มีสไตล์ชีทที่บล็อกสคริปต์องค์ประกอบนั้นเป็น สคริปต์การบล็อกการแยกวิเคราะห์ที่ค้างอยู่ของเอกสารของตัวแยกวิเคราะห์ที่สร้างองค์ประกอบ (สามารถมีได้เพียงหนึ่งสคริปต์ต่อเอกสารในแต่ละครั้ง)

ตั้งค่าสถานะ "พร้อมที่จะแยกวิเคราะห์" ขององค์ประกอบ parser จะจัดการกับการดำเนินการสคริปต์

หากองค์ประกอบมีแอตทริบิวต์ src ไม่มีแอตทริบิวต์ async และไม่มีการตั้งค่าสถานะ "force-async"องค์ประกอบจะต้องเพิ่มในส่วนท้ายของรายการสคริปต์ที่จะดำเนินการตามลำดับโดยเร็วที่สุดเท่าที่จะเป็นไปได้ ด้วยเอกสารขององค์ประกอบสคริปต์ในเวลาที่การเตรียมอัลกอริทึมสคริปต์เริ่มต้นขึ้น

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

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

การดำเนินการ: ดำเนินการบล็อกสคริปต์ที่สอดคล้องกับองค์ประกอบสคริปต์แรกในรายการสคริปต์นี้ที่จะดำเนินการตามลำดับโดยเร็วที่สุด

ลบองค์ประกอบแรกออกจากรายการสคริปต์นี้ที่จะดำเนินการตามลำดับโดยเร็วที่สุด

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

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

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

มิฉะนั้นเอเจนต์ผู้ใช้จะต้องเรียกใช้งานบล็อกสคริปต์ทันทีแม้ว่าสคริปต์อื่นกำลังดำเนินการอยู่


สิ่งที่เกี่ยวกับสคริปต์โมดูล Javascript type="module"?

ตอนนี้ Javascript รองรับการโหลดโมดูลด้วยไวยากรณ์ดังนี้:

<script type="module">
  import {addTextToBody} from './utils.mjs';

  addTextToBody('Modules are pretty cool.');
</script>

หรือด้วยsrcแอตทริบิวต์:

<script type="module" src="http://somedomain.com/somescript.mjs">
</script>

สคริปต์ทั้งหมดที่มีtype="module"จะได้รับdeferแอตทริบิวต์โดยอัตโนมัติ สิ่งนี้จะดาวน์โหลดแบบขนาน (หากไม่ใช่แบบอินไลน์) พร้อมการโหลดหน้าอื่น ๆ จากนั้นเรียกใช้ตามลำดับ แต่หลังจากโปรแกรมแยกวิเคราะห์เสร็จแล้ว

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

: มีแผนภูมิระยะเวลาที่มีประโยชน์สวยที่แสดงให้เห็นว่าสามารถดึงข้อมูลและการดำเนินการแตกต่างกันของสคริปต์รวมทั้งสคริปต์โมดูลที่นี่ในบทความนี้เป็นJavascript โมดูลโหลด


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

@Bergi - หากมีการเพิ่มแบบไดนามิกมันเป็น async และลำดับการดำเนินการจะไม่สามารถระบุได้เว้นแต่คุณจะเขียนรหัสเพื่อควบคุม
jfriend00

เพียงKolink ระบุตรงข้าม ...
Bergi

@Bergi - ตกลงฉันได้แก้ไขคำตอบของฉันเพื่อบอกว่าสคริปต์ async โหลดตามลำดับที่กำหนด สามารถโหลดได้ในลำดับใดก็ได้ ถ้าฉันเป็นคุณฉันจะไม่เชื่อในการสังเกตของ Kolink ว่ามันเป็นอย่างนั้นเสมอไป ฉันรู้ว่าไม่มีมาตรฐานที่ระบุว่าสคริปต์ที่เพิ่มแบบไดนามิกจะต้องทำงานทันทีและจะต้องปิดกั้นสคริปต์อื่น ๆ ไม่ให้ทำงานจนกว่าจะโหลด ฉันคาดว่าจะขึ้นอยู่กับเบราว์เซอร์และอาจขึ้นอยู่กับปัจจัยด้านสิ่งแวดล้อมด้วย (ไม่ว่าจะเป็นสคริปต์แคช ฯลฯ ... )
jfriend00

1
@RuudLenders - ขึ้นอยู่กับการนำเบราว์เซอร์มาใช้ การพบแท็กสคริปต์ก่อนหน้าในเอกสาร แต่ทำเครื่องหมายด้วยdeferเปิดโอกาสให้ parser เริ่มดาวน์โหลดได้เร็วขึ้นในขณะที่ยังคงเลื่อนการทำงาน โปรดทราบว่าถ้าคุณมีจำนวนมากสคริปต์จากโฮสต์เดียวกันแล้วเริ่มต้นการดาวน์โหลดเร็วจริงอาจชะลอตัวลงในการดาวน์โหลดของผู้อื่นจากโฮสต์เดียวกัน (เช่นที่พวกเขาในการแข่งขันสำหรับแบนด์วิดธ์) ที่หน้าของคุณจะรอ (ที่ไม่ได้defer) เพื่อให้ นี่อาจเป็นดาบสองคม
jfriend00

13

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

เพื่อทดสอบข้อเท็จจริงนี้:

// file: test.php
sleep(10);
die("alert('Done!');");

// HTML file:
<script type="text/javascript" src="test.php"></script>

สคริปต์ที่เพิ่มแบบไดนามิกจะถูกดำเนินการทันทีที่ผนวกเข้ากับเอกสาร

เพื่อทดสอบข้อเท็จจริงนี้:

<!DOCTYPE HTML>
<html>
<head>
    <title>Test</title>
</head>
<body>
    <script type="text/javascript">
        var s = document.createElement('script');
        s.type = "text/javascript";
        s.src = "link.js"; // file contains alert("hello!");
        document.body.appendChild(s);
        alert("appended");
    </script>
    <script type="text/javascript">
        alert("final");
    </script>
</body>
</html>

ลำดับของการเตือนคือ "ต่อท้าย" -> "สวัสดี!" -> "รอบชิงชนะเลิศ"

หากในสคริปต์คุณพยายามเข้าถึงองค์ประกอบที่ยังไม่ได้เข้าถึง (ตัวอย่าง:) <script>do something with #blah</script><div id="blah"></div>จากนั้นคุณจะได้รับข้อผิดพลาด

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


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

4
link.js ไม่ได้ปิดกั้น ใช้สคริปต์ที่คล้ายกับ php ของคุณเพื่อจำลองการดาวน์โหลดเป็นเวลานาน
1983

14
คำตอบนี้ไม่ถูกต้อง ไม่ใช่กรณีที่ "สคริปต์ที่เพิ่มแบบไดนามิกจะถูกดำเนินการทันทีที่ผนวกเข้ากับเอกสาร" บางครั้งสิ่งนี้เป็นจริง (เช่นสำหรับ Firefox รุ่นเก่า) แต่โดยปกติจะไม่เป็นเช่นนั้น ลำดับการดำเนินการตามที่กล่าวไว้ในคำตอบของ jfriend00 ไม่ได้ถูกกำหนดไว้
Fabio Beltramini

1
ไม่เหมาะสมที่สคริปต์จะถูก excecuted ตามลำดับที่ปรากฏบนหน้าโดยไม่คำนึงว่าเป็นแบบอินไลน์หรือไม่ เหตุใดข้อมูลโค้ดเครื่องมือจัดการแท็กของ Google และอื่น ๆ อีกมากมายที่ฉันเคยเห็นมีรหัสให้แทรกสคริปต์ใหม่เหนือแท็กสคริปต์อื่นทั้งหมดในหน้า คงไม่สมเหตุสมผลถ้าสคริปต์ด้านบนโหลดเรียบร้อยแล้ว หรือฉันขาดอะไรไป
3094826


2

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

loadScripts(sources) {
    sources.forEach(src => {
        var script = document.createElement('script');
        script.src = src;
        script.async = false; //<-- the important part
        document.body.appendChild( script ); //<-- make sure to append to body instead of head 
    });
}

loadScripts(['/scr/script1.js','src/script2.js'])
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.