<script defer =“ defer”> ทำงานอย่างไร


208

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

เพื่อทดสอบผมดำเนินการนี้บน Chrome: http://jsfiddle.net/xXZMN/

<script defer="defer">alert(2);</script>
<script>alert(1)</script>
<script defer="defer">alert(3);</script>

2 - 1 - 3แต่จะแจ้งเตือน ทำไมมันไม่แจ้งเตือน1 - 2 - 3?


2
อาจจะตรวจสอบบทความนี้ และเช่นเคย IE มีสิ่งที่ตัวเองหมายถึงและตัดสินใจที่จะโหลดสคริปต์ก่อน แต่การประวิงเวลาดำเนินการจนกว่าจะมีการโหลดเนื้อหา (โดยทั่วไป)
แบรดคริสตี้

ขอบคุณ แต่หน้าทดสอบมีผลที่แตกต่างกันบน Chrome: websiteoptimization.com/speed/tweak/defer/test ภาพหน้าจอแสดงให้เห็นว่าฉันคาดหวังได้อย่างไรในขณะที่ Chrome ดูเหมือนว่าจะดำเนินการรอการตัดบัญชีก่อน
pimvdb

1
ฉันคิดว่าคุณจะพบคำจำกัดความการเลื่อนเวลาของ IE ตรงกับความตั้งใจของ W3C ในการเลื่อนการเลื่อนระดับ DOM 1
ทำเครื่องหมายที่ Ramp51

41
ในฐานะที่เป็น Alohci แล้วชี้ให้เห็นในคำตอบของเขาเป็นไปตามมาตรฐาน HTML จะใช้ได้เฉพาะเมื่อระบุdefer srcนี่อาจเป็นเหตุผลว่าทำไมตัวอย่างของคุณไม่ทำงานตามที่คาดไว้ในเบราว์เซอร์ส่วนใหญ่
Pankrat

2
@Pankrat เรื่องจริง! ลองjsfiddle.net/xXZMN/50ทดสอบใน Firefox24
m93a

คำตอบ:


51

อัปเดต: 2/19/2016

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


โดยพื้นฐานเลื่อนการแจ้งให้เบราว์เซอร์รอ "จนกว่าจะพร้อม" ก่อนดำเนินการจาวาสคริปต์ในบล็อกสคริปต์นั้น โดยปกตินี่คือหลังจากที่ DOM เสร็จสิ้นการโหลดและ document.readyState == 4

แอตทริบิวต์ defer นั้นขึ้นอยู่กับ internet explorer ใน Internet Explorer 8 บน Windows 7 ผลลัพธ์ที่ฉันเห็นในหน้าทดสอบ JS Fiddle ของคุณคือ 1 - 2 - 3

ผลลัพธ์อาจแตกต่างกันไปในแต่ละเบราว์เซอร์

http://msdn.microsoft.com/en-us/library/ms533719(v=vs.85).aspx

ตรงกันข้ามกับความเชื่อที่เป็นที่นิยม IE ปฏิบัติตามมาตรฐานบ่อยกว่าที่คนอื่นให้ไว้ในความเป็นจริงแอตทริบิวต์ "เลื่อน" ถูกกำหนดในข้อมูลจำเพาะ DOM ระดับ 1 http://www.w3.org/TR/REC-DOM-Level-1/level -One-html.html

คำจำกัดความการเลื่อนเวลาของ W3C: http://www.w3.org/TR/REC-html40/interact/scripts.html#adef-defer :

"เมื่อตั้งค่าแอตทริบิวต์บูลีนนี้จะให้คำแนะนำแก่ตัวแทนผู้ใช้ว่าสคริปต์จะไม่สร้างเนื้อหาเอกสารใด ๆ (เช่นไม่มี" document.write "ใน javascript) ดังนั้นตัวแทนผู้ใช้สามารถแยกวิเคราะห์และแสดงผลได้"


8
@ MarkAtRamp51 - หากคำตอบของคุณล้าสมัยคุณควรแก้ไขแทนที่จะบ่นเกี่ยวกับ downvotes ในความคิดเห็นเกี่ยวกับคำตอบอื่น ๆ Downvotes สำหรับคำตอบที่ "ไม่มีประโยชน์"
Christian Conkle

10
@ChristianConkle ฉันขอขอบคุณบทเรียนมารยาทอย่างไรก็ตามคำตอบอื่น ๆ ที่นี่มีถึงวันที่ ฉันกำลังพูดถึงข้อเท็จจริงว่าไม่ได้เลือกคำตอบที่ผิดในขณะที่ถามคำถาม บางทีคุณควรตำรวจให้ผู้คนกระจายการประเมินที่ผิด ๆ เกี่ยวกับชุมชนที่เลือกคำตอบอย่างไม่เหมาะสมแทนที่จะเป็นคนที่พยายามเตือนผู้คนว่าสิ่งต่าง ๆ เปลี่ยนแปลงไปตามกาลเวลาและบริบทเป็นสิ่งสำคัญ ฉันไม่เห็นคุณค่าในการลบคำตอบเนื่องจากข้อมูลในอดีตมีค่าเช่นกัน
ทำเครื่องหมายที่ Ramp51

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

3
@Leo ไม่ควรตั้งค่าสถานะแล้ว? โดยการค้นหา "html5 defer script" นี่เป็นผลลัพธ์ที่สามใน google คำตอบนี้ให้ผู้ใช้จำนวนมากที่ล้าสมัยและคำจำกัดความไม่ถูกต้อง (คำจำกัดความปัจจุบัน: "บ่งชี้ว่าตัวแทนผู้ใช้สามารถเลื่อนการประมวลผลของสคริปต์ดูคำจำกัดความของแอตทริบิวต์ defer ใน HTML 4.0")
Malavos

2
@ MarkAtRamp51 ฉันคิดว่าคุณควรอัปเดตคำตอบของคุณ ใครก็ตามที่พบคำถามนี้ดังนั้นคำตอบของคุณจะไม่รับรู้ข้อมูลในอดีต มันจะดูเหมือนพวกเขาเหมือนคำตอบที่ถูกต้องในวันนี้ นั่นเป็นวิธีที่อินเทอร์เน็ตทำงาน ดังนั้นคุณควรแก้ไขคำตอบของคุณโปรดทราบว่ามันถูกต้องเป็นครั้งคราวและอ้างถึงคำตอบที่ถูกต้อง
Juuro

167

ตัวอย่างบางส่วนจากข้อกำหนด HTML5: http://w3c.github.io/html/semantics-scripting.html#element-attrdef-script-async

ต้องไม่ระบุแอตทริบิวต์ defer และ async หากไม่มีแอตทริบิวต์ src


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


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


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

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


37
บางทีการตอบกลับของฉันที่นี่จะหยุดไม่ให้ผู้คนโหวตให้คำตอบของฉันเนื่องจากความคิดเห็นของคุณไม่เป็นประโยชน์ คำตอบที่ยอมรับไม่ผิดคำตอบนั้นแตกต่างกันเนื่องจากในช่วงต้นปี 2011 ข้อมูลจำเพาะ HTML5 นั้นมีความเกี่ยวข้องน้อยกว่าเว็บเบราว์เซอร์หลักมากกว่าที่เป็นอยู่ในปัจจุบัน คำตอบนี้อาจจะดีกว่าไปข้างหน้า แต่คำตอบที่ยอมรับไม่ได้ผิดมาตรฐานใด ๆ
ทำเครื่องหมายที่ Ramp51

3
ในขณะที่มันมีประโยชน์ที่จะรู้ว่าสิ่งที่สเปคกล่าวว่าปรากฎว่าบางเบราว์เซอร์เช่น IE <9 ดำเนินการdeferไม่ดี หากคุณใช้deferคุณจะไม่สามารถพึ่งพาไฟล์สคริปต์ที่ถูกเรียกใช้งานตามลำดับในบางเบราว์เซอร์
Flimm

2
@Flimm ไม่เพียง IE มันก็ดูเหมือนว่าคำสั่งที่จะไม่รับประกันใน Firefox อย่างใดอย่างหนึ่ง
Franklin Yu

อ้างแรกไม่ถูกต้องอีกต่อไปใช่มั้ย ตอนนี้ฉันสามารถอ่านสิ่งนี้ได้: "ต้องไม่ระบุแอตทริบิวต์หากไม่มีแอตทริบิวต์ src หรือสคริปต์ไม่ได้เป็นสคริปต์แบบคลาสสิก" และสคริปต์คลาสสิคก็เป็นสคริปต์ที่ไม่มี src = "" ด้วย
Félix Sanz

158

คำตอบที่แท้จริงคือ: เพราะคุณไม่สามารถไว้วางใจเลื่อนเวลาออกไปได้

ในแนวคิดเลื่อนและ async แตกต่างกันดังนี้:

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

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

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

  • ในบางสถานการณ์เบราว์เซอร์บางตัวมีข้อผิดพลาดที่ทำให้deferสคริปต์หมด
  • เบราว์เซอร์บางตัวชะลอDOMContentLoadedกิจกรรมจนกว่าdeferจะโหลดสคริปต์เสร็จและบางโปรแกรมไม่ทำงาน
  • เบราว์เซอร์บางเชื่อฟังdeferใน<script>องค์ประกอบที่มีรหัสแบบอินไลน์และไม่มีsrcแอตทริบิวต์และบางคนไม่สนใจมัน

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

<script defer async src="..."></script>

98% ของเบราว์เซอร์ที่ใช้ทั่วโลกและ 99% ในสหรัฐอเมริกาจะหลีกเลี่ยงการบล็อกด้วยวิธีนี้

(หากคุณต้องรอจนกว่าเอกสารจะเสร็จสิ้นการแยกวิเคราะห์ฟังเหตุการณ์DOMContentLoadedเหตุการณ์หรือใช้.ready()ฟังก์ชั่นที่ใช้งานง่ายของ jQuery คุณต้องการทำสิ่งนี้ต่อไปเพื่อให้เบราว์เซอร์ที่ไม่ได้ใช้งานมีdeferประสิทธิภาพ)


13
ขอบคุณคำตอบของคุณมีประโยชน์ที่สุดสำหรับฉัน!
markus

5
ฉันเชื่อว่านี่ไม่ถูกต้อง ประโยชน์ของการเลื่อนเวลาคือมันจะไม่ทำงานจนกว่าการวิเคราะห์หน้าจะเสร็จสมบูรณ์ หน้านี้มีภาพที่ดีในการอธิบายความแตกต่างระหว่าง async และ defer: peter.sh/experiments/ …
tinkerr

1
@tinkerr ในแนวคิดของคุณถูกต้อง; ในทางปฏิบัติสิ่งนี้ไม่ได้กลายเป็นจริง เนื่องจากมันไม่ได้ถูกนำมาใช้อย่างสม่ำเสมอการรับประกันแบบลำดับนั้นไม่เป็นสากลดังนั้นจึงไม่ใช่การรับประกัน เมื่อใช้สิ่งที่คุณสนใจเกี่ยวกับการดำเนินการ ความตั้งใจของการออกแบบนั้นน่ารัก แต่ก็ไม่ได้มีประโยชน์อะไรเป็นพิเศษ
Chris Moschini

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

1
@VikasBansal สำหรับเบราว์เซอร์รุ่นเก่าที่ไม่สนับสนุน async - คือ IE ที่เก่ากว่า
Chris Moschini

13

deferสามารถใช้ใน<script>แท็กสำหรับการรวมสคริปต์ภายนอกเท่านั้น ดังนั้นขอแนะนำให้ใช้ใน-tags <script>ใน<head>-section


8

เนื่องจากคุณสมบัติการเลื่อนออกไปทำงานกับแท็กสคริปต์ที่มี src พบวิธีในการเลียนแบบการเลื่อนสคริปต์แบบอินไลน์ ใช้เหตุการณ์ DOMContentLoaded

<script defer src="external-script.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
    // Your inline scripts which uses methods from external-scripts.
});
</script>

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


6

แอตทริบิวต์ defer ใช้สำหรับสคริปต์ภายนอกเท่านั้น (ควรใช้ต่อเมื่อมีแอตทริบิวต์ src อยู่)



4

ดูบทความที่ยอดเยี่ยมนี้ดำดิ่งลงไปในน้ำที่เต็มไปด้วยหมอกของการโหลดสคริปต์โดยนักพัฒนาของ Google Jake Archibald เขียนในปี 2013

การอ้างถึงส่วนที่เกี่ยวข้องจากบทความนั้น:

Defer

<script src="//other-domain.com/1.js" defer></script>
<script src="2.js" defer></script>

Spec พูดว่า : ดาวน์โหลดด้วยกันดำเนินการตามลำดับก่อน DOMContentLoaded ละเว้น "เลื่อน" ไปที่สคริปต์ที่ไม่มี "src"

IE <10 พูดว่า : ฉันอาจรัน 2.js ครึ่งทางผ่านการทำงานของ 1.js. ไม่สนุกเหรอ?

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

เบราว์เซอร์อื่น ๆ พูดว่า : โอเค แต่ฉันไม่อาจเพิกเฉยต่อการ "เลื่อน" สคริปต์ที่ไม่มี "src"

(ฉันจะเพิ่ม Firefox เวอร์ชันก่อนหน้าของทริกเกอร์ DOMContentLoaded ก่อนที่deferสคริปต์จะทำงานเสร็จตามความคิดเห็นนี้)

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


1

แอ็ตทริบิวต์ Boolean นี้ถูกตั้งค่าให้บ่งบอกถึงเบราว์เซอร์ที่สคริปต์นั้นตั้งใจให้เรียกใช้งานหลังจากเอกสารถูกวิเคราะห์ เนื่องจากคุณสมบัตินี้ยังไม่ได้นำมาใช้กับเบราว์เซอร์หลักอื่น ๆ ผู้เขียนจึงไม่ควรสันนิษฐานว่าการดำเนินการของสคริปต์จะถูกเลื่อนออกไป ไม่เคยเรียก document.write () จากสคริปต์เลื่อน (ตั้งแต่ Gecko 1.9.2 สิ่งนี้จะทำให้เอกสารหายไป) ไม่ควรใช้แอตทริบิวต์ defer กับสคริปต์ที่ไม่มีแอตทริบิวต์ src ตั้งแต่ Gecko 1.9.2 แอ็ตทริบิวต์ defer จะถูกข้ามไปที่สคริปต์ที่ไม่มีแอ็ตทริบิวต์ src อย่างไรก็ตามใน Gecko 1.9.1 แม้สคริปต์แบบอินไลน์จะถูกเลื่อนออกไปหากมีการตั้งค่าแอตทริบิวต์ defer

เลื่อนการทำงานกับ chrome, firefox เช่น> 7 และ Safari

ref: https://developer.mozilla.org/en-US/docs/HTML/Element/script


0

แอตทริบิวต์ defer เป็นแอตทริบิวต์แบบบูล

เมื่อมีการระบุว่าสคริปต์จะถูกดำเนินการเมื่อหน้าได้เสร็จสิ้นการแยก

หมายเหตุ: แอ็ตทริบิวต์ defer มีไว้สำหรับสคริปต์ภายนอกเท่านั้น (ควรใช้เฉพาะเมื่อมีคุณสมบัติ src อยู่)

หมายเหตุ: มีหลายวิธีที่สคริปต์ภายนอกสามารถดำเนินการได้:

หาก async มีอยู่: สคริปต์จะถูกดำเนินการแบบอะซิงโครนัสกับส่วนที่เหลือของหน้า (สคริปต์จะถูกดำเนินการในขณะที่หน้ายังคงดำเนินการแยกวิเคราะห์) หากไม่มี async อยู่และเลื่อนออก: สคริปต์จะถูกดำเนินการ ไม่มี async หรือ defer อยู่: สคริปต์จะถูกดึงข้อมูลและดำเนินการทันทีก่อนที่เบราว์เซอร์จะแยกวิเคราะห์หน้าต่อไป

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.