วันนี้มีคนถามฉันว่าจะใช้แถบความคืบหน้าในการโหลด Javascript เช่นกล่องจดหมาย 163 ได้อย่างไร
ฉันไม่รู้ แต่มันก็ไม่ยากเลยที่จะนำไปใช้ เพราะ <script /> มี onload และ onreadystatechange นอกจากนี้เรายังมีแอตลาส
มีคลาสใน Atlas: Sys.ScriptLoader หน้าที่ของมันคือการโหลดไฟล์สคริปต์หลายไฟล์ในเพจตามลำดับ ก่อนที่จะนำไปใช้เรามาวิเคราะห์โค้ดของคลาสนี้กันก่อน
1Sys.ScriptLoader = ฟังก์ชั่น() {
2
3 //อาร์เรย์ของวัตถุอ้างอิงสำหรับสคริปต์ทั้งหมด
4 var _references;
5 // ฟังก์ชั่น Callback ดำเนินการหลังจากโหลดสคริปต์ทั้งหมดแล้ว
6 var _completionCallback;
7 // บริบท (พารามิเตอร์) ที่ให้ไว้เมื่อเรียกใช้ฟังก์ชันโทรกลับ
8 var _callbackContext;
9
10 // องค์ประกอบ HTTP (<script />) ของสคริปต์ที่กำลังโหลดอยู่
11 คือ _currentLoadingReference;
12 // ฟังก์ชั่นโทรกลับที่ถูกเรียกหลังจากโหลดสคริปต์ปัจจุบัน
13 var _currentOnScriptLoad;
14
15 // วิธีเดียวของ ScriptLoader คือการส่งผ่านพารามิเตอร์สามตัว ความหมายของพารามิเตอร์จะไม่ถูกทำซ้ำ
16 this.load = ฟังก์ชั่น (อ้างอิง, เสร็จสิ้นการโทรกลับ, callbackContext) {
17 _references = ข้อมูลอ้างอิง;
18 _completionCallback = โทรกลับเสร็จสิ้น;
19 _callbackContext = คอนเท็กซ์เรียกกลับ;
20
21 โหลดอ้างอิง();
ยี่สิบสอง }
ยี่สิบสาม
24 // เริ่มโหลดข้อมูลอ้างอิง
25 ฟังก์ชั่น loadReferences() {
26 // หากสคริปต์กำลังโหลดอยู่
27 // ซึ่งหมายความว่าวิธีนี้ไม่ได้ถูกเรียกเป็นครั้งแรก แต่ถูกโหลดในสคริปต์
28 // เรียกหลังจากเสร็จสิ้นเพื่อโหลดสคริปต์ถัดไป
29 ถ้า (_currentLoadingReference) {
30 // ตรวจสอบ readyState ขององค์ประกอบสคริปต์ปัจจุบัน ซึ่งเสร็จสมบูรณ์ภายใต้ IE
31 // โหลดเบราว์เซอร์อื่นเช่น FF แล้ว (จริงๆ แล้ว FF ไม่มีแอตทริบิวต์นี้
32 // แต่โค้ดด้านล่างนี้จะกำหนดให้โหลด)
33 // หากการโหลดล้มเหลว ให้ออก
34 ถ้า ((_currentLoadingReference.readyState != 'โหลดแล้ว') &&
35 (_currentLoadingReference.readyState != 'สมบูรณ์')) {
36 กลับ;
37 }
38 อื่น ๆ {
39 // เมื่อเข้าสู่สาขานี้แสดงว่าโหลดสำเร็จ
40
41 // หากสคริปต์ปัจจุบันกำหนดฟังก์ชัน onLoad
42 ถ้า (_currentOnScriptLoad) {
43 // โทรผ่าน eval (นี่คือปัญหา)
44 eval(_currentOnScriptLoad);
45 //ตั้งค่าเป็น null เพื่อปล่อยทรัพยากร
46 _currentOnScriptLoad = โมฆะ;
47 }
48
49 // ตั้งค่าเหตุการณ์ที่เกี่ยวข้องเป็นโมฆะเพื่อให้แน่ใจว่าทรัพยากรถูกเผยแพร่
50 ถ้า (Sys.Runtime.get_hostType() != Sys.HostType.InternetExplorer) {
51 // หากเบราว์เซอร์ปัจจุบันไม่ใช่ IE โปรดดูโค้ดด้านล่าง
52 // คุณจะพบว่าเหตุการณ์ onload ถูกกำหนดไว้สำหรับ <script />
53 _currentLoadingReference.onload = null;
54 }
55 อื่น ๆ {
56 // หากเป็น IE ดูโค้ดด้านล่างแล้วคุณจะพบ
57 // <script /> กำหนดเหตุการณ์ onreadystatechange
58 _currentLoadingReference.onreadystatechange = null;
59 }
60
61 //ในที่สุดก็ปล่อยการอ้างอิง <script /> ปัจจุบัน
62 _currentLoadingReference = null;
63}
64}
65
66 // หากยังมีสคริปต์ที่ไม่ได้โหลดอยู่
67 ถ้า (_references.length) {
68 // หมดคิว.
69 การอ้างอิง var = _references.dequeue();
70 // สร้าง <script />
71 var scriptElement = document.createElement('script');
72 //ตั้งค่า <script /> ปัจจุบันและฟังก์ชั่นการโทรกลับปัจจุบันเพื่อให้การโหลดสำเร็จ
73 _currentLoadingReference = scriptElement;
74 _currentOnScriptLoad = การอ้างอิง.onscriptload;
75
76 ถ้า (Sys.Runtime.get_hostType() != Sys.HostType.InternetExplorer) {
77 // หากไม่ใช่ IE ให้ตั้งค่าแอตทริบิวต์ readyState สำหรับ <script />
78 // และใช้เหตุการณ์ onload
79 scriptElement.readyState = 'โหลดแล้ว';
80 scriptElement.onload = loadReferences;
81 }
82 อื่น ๆ {
83 // ถ้าเป็น IE ให้ใช้เหตุการณ์ onreadystatechange
84 scriptElement.onreadystatechange = loadReferences;
85}
86 scriptElement.type = 'ข้อความ/จาวาสคริปต์';
87 scriptElement.src = อ้างอิง URL;
88
89 // เพิ่ม <script /> ใน DOM
90 var headElement = document.getElementsByTagName('head')[0];
91 headElement.appendChild(scriptElement);
92
93 กลับ;
94}
95
96 // หากการดำเนินการมาถึงจุดนี้ แสดงว่าสคริปต์ทั้งหมดถูกโหลดแล้ว
97 // หากมีการกำหนดฟังก์ชันการเรียกกลับที่ดำเนินการหลังจากโหลดสคริปต์ทั้งหมดแล้ว
98 // จากนั้นดำเนินการและปล่อยทรัพยากร
99 ถ้า (_completionCallback) {
100 เสร็จสิ้นCallback = _completionCallback;
101 คือ callbackContext = _callbackContext;
102
103 _completionCallback = โมฆะ;
104 _callbackContext = โมฆะ;
105
106 เสร็จสิ้นการโทรกลับ (callbackContext);
107 }
108
109 _references = โมฆะ;
110 }
111}
112Sys.ScriptLoader.registerClass('Sys.ScriptLoader');
จะเห็นได้ว่าวิธีการสำหรับ Sys.ScriptLoader ในการโหลดสคริปต์คือการเพิ่มองค์ประกอบ <script /> ให้กับ <header /> ตามลำดับผ่านโค้ด อันที่จริงมันไม่ค่อยมีการใช้งานใน Atlas
อันที่จริง โค้ดของ Sys.ScriptLoader นั้นเรียบง่ายมากและความคิดเห็นที่ฉันเพิ่มดูเหมือนจะไม่จำเป็น เป็นที่น่าสังเกตว่าทรัพยากรทั้งหมดจะถูกปล่อยออกมาให้มากที่สุด ให้ความสนใจเป็นพิเศษกับโค้ดที่เริ่มต้นจากบรรทัดที่ 99 อันดับแรกเนื้อหา if จะใช้ตัวแปรชั่วคราวเพื่อรักษาตัวแปรร่วมสองตัวไว้ จากนั้นจึงปล่อยตัวแปรส่วนกลาง จุดประสงค์คือเพื่อหลีกเลี่ยงการรั่วไหลของหน่วยความจำที่เกิดจากข้อยกเว้นที่เกิดขึ้นเมื่อมีการดำเนินการ Callback เสร็จสมบูรณ์ แม้ว่าจะมีความเป็นไปได้เพียงหนึ่งในหมื่นก็ตาม ยิ่งมี Javascript มากเท่าไหร่ ก็ยิ่งทำให้หน่วยความจำรั่วได้ง่ายขึ้นเท่านั้น วิธีที่ดีที่สุดคือให้ความสนใจกับปัญหานี้เมื่อเขียนโค้ด JS
ต่อไป อธิบายพารามิเตอร์แรกของวิธีการโหลด การอ้างอิง เดิมทีฉันคิดว่านี่คืออาร์เรย์ของคลาส Sys.Reference แต่ฉันพบว่าจริงๆ แล้วมันแตกต่างออกไปมาก ยังไงก็ลองดูโค้ดของคลาสนี้ดูครับ
1Sys.Reference = ฟังก์ชั่น () {
2
3 var _ส่วนประกอบ;
4 var _onload;
5
6 this.get_component = ฟังก์ชั่น() {
7 กลับ _ส่วนประกอบ;
8}
9 this.set_component = ฟังก์ชั่น (ค่า) {
10 _ส่วนประกอบ = ค่า;
11 }
12
13 this.get_onscriptload = ฟังก์ชั่น() {
14 กลับ _onload;
15}
16 this.set_onscriptload = ฟังก์ชั่น (ค่า) {
17 _onload = ค่า;
18}
19
20 this.dispose = ฟังก์ชั่น() {
21 _ส่วนประกอบ = โมฆะ;
ยี่สิบสอง }
ยี่สิบสาม
24 this.getDescriptor = ฟังก์ชั่น() {
25 var td = Sys.TypeDescriptor ใหม่ ();
26
27 td.addProperty('ส่วนประกอบ', วัตถุ);
28 td.addProperty('onscriptload', สตริง);
29 กลับ td;
30}
31}
32Sys.Reference.registerSealedClass('Sys.Reference', null, Sys.ITypeDescriptorProvider, Sys.IDisposable);
33Sys.TypeDescriptor.addType('สคริปต์', 'อ้างอิง', Sys.Reference);
เมื่อให้ความสนใจกับโค้ดของคลาส Sys.ScriptLoader เราจะเห็นว่าแต่ละองค์ประกอบของอาร์เรย์อ้างอิงนั้นแท้จริงแล้วเป็นเพียง "{ url : " http://www.sample.com/sample.js ", onscriptload : " alert(1)"}" ในรูปแบบวัตถุ แต่ก็ไม่เป็นไร คุณสามารถใช้ JSON เพื่อสร้างอาร์เรย์ดังกล่าวได้อย่างง่ายดาย
ณ จุดนี้ ฉันคิดว่าทุกคนควรคิดถึงวิธีใช้ Sys.ScriptLoader เพื่อสร้างแถบความคืบหน้าในการโหลด JS ได้อย่างง่ายดาย แต่ตอนนี้ฉันได้เขียนไว้ที่นี่แล้ว ฉันจะนำไปปฏิบัติด้วยวิธีง่ายๆ ต่อไป
อย่างแรกคือไฟล์ aspx
1<%@ ภาษาของเพจ = "C#" %>
2
3<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd ">
4
5<script runat="เซิร์ฟเวอร์">
6
7</สคริปต์>
8
9<html xmlns=" http://www.w3.org/1999/xhtml " >
10<หัว runat="เซิร์ฟเวอร์">
11 <title>โหลดสคริปต์</title>
12 <ภาษาสคริปต์="javascript">
โหลด 13 ฟังก์ชั่น()
14 {
15 document.getElementById("bar").style.width = "0px";
สคริปต์ 16 var = อาร์เรย์ใหม่ ();
17 สำหรับ (var i = 0; i < 8; i++)
18 {
19 var s = วัตถุใหม่ ();
20 เท่ากับ sleep = Math.round((Math.random() * 400)) + 100;
21 s.url = "Script.ashx?sleep=" + sleep + "&t=" + Math.random();
22 s.cost = นอน;
23 scripts.push (s);
ยี่สิบสี่ }
25
26 Jeffz.Sample.LoadScripts.load(สคริปต์);
27}
28 </สคริปต์>
29</หัว>
30<body style="font-family: Arial;">
31 <form id="form1" runat="server">
32 <div>
33 <atlas:ScriptManager ID="ScriptManager1" runat="เซิร์ฟเวอร์">
34 <สคริปต์>
35 <atlas:ScriptReference Path="js/LoadScripts.js" />
36 </สคริปต์>
37 </atlas:ScriptManager>
38
39 แถบความคืบหน้า:
40 <div style="border: solid 1px black;">
41 <div id="bar" style="height: 20px; width:0%; background-color:Red;"></div>
42 </div>
43 <input type="button" onclick="Load()" value="Load" />
44 <div id="ข้อความ"></div>
45 </div>
46 </แบบฟอร์ม>
47</ร่างกาย>
48</html>
ง่ายมาก แถบความคืบหน้าที่ง่ายที่สุดถูกสร้างขึ้นโดยใช้สอง DIV ฟังก์ชัน Load() จะถูกเรียกเมื่อมีการคลิกปุ่ม ฟังก์ชันนี้สุ่มสร้างลิงก์สคริปต์และสร้างอาร์เรย์สคริปต์ 8 องค์ประกอบ รูปแบบของอาร์เรย์สคริปต์จะเป็นดังนี้:
1var scripts =
2[
3 { URL : " http://www.sample.com/sample1.js ", ต้นทุน : costOfLoading1 },
4 { URL : " http://www.sample.com/sample2.js ", ราคา : costOfLoading2 },
5 { URL : " http://www.sample.com/sample3.js " ต้นทุน : costOfLoading3 }
6];
ไม่จำเป็นต้องพูดว่าแอตทริบิวต์ url ของแต่ละองค์ประกอบและฟังก์ชันของต้นทุนคือการแสดงถึงมูลค่าของเวลาที่ใช้ในการโหลดไฟล์ ค่านี้ไม่มีหน่วย จะใช้เฉพาะสัดส่วนของค่านี้ในการใช้งานทั้งหมดเท่านั้น นอกจากนี้ คุณจะเห็นว่ามี Script.ashx ซึ่งใช้ในการจำลองการโหลดสคริปต์แบบยาว โดยจะพักเธรดเป็นระยะเวลาหนึ่งตามค่าของ sleep ในสตริงการสืบค้น (สำหรับ t ต่อไปนี้ จุดประสงค์คือเพียงเพื่อหลีกเลี่ยงการคลิกปุ่มโดยเปลี่ยนแคชของเบราว์เซอร์สตริงการสืบค้น) ไฟล์นี้แทบไม่มีโค้ดเลยและสามารถเห็นการใช้งานได้ในการดาวน์โหลดตัวอย่าง สุดท้ายนี้จะถูกโหลดโดยการเรียกเมธอด Jeffz.Sample.LoadScripts.load ซึ่งเกี่ยวข้องกับโค้ดต่อไปนี้ LoadScripts.js:
1Type.registerNamespace('Jeffz.Sample');
2
3Jeffz.Sample.LoadScripts = ฟังก์ชันใหม่()
4{
5 เท่ากับต้นทุนทั้งหมด = 0;
6 var scriptLoader = Sys.ScriptLoader ใหม่ ();
7
8 this.load = ฟังก์ชั่น (สคริปต์)
9 {
10 ถ้า (Jeffz.Sample.__onScriptLoad != null)
11 {
12 โยนข้อผิดพลาดใหม่ ("กำลังดำเนินการ");
13}
14
15 ต้นทุนทั้งหมด = 0;
16 Jeffz.Sample.__onScriptLoad = onScriptLoad;
การอ้างอิง 17 รายการ = new Array();
18
19 มูลค่าการโหลด = 0;
20 สำหรับ (var i = 0; i < scripts.length; i++)
ยี่สิบเอ็ด {
22 รวมต้นทุน += สคริปต์ [i].ต้นทุน;
23 LoadCost += สคริปต์ [i] .cost;
ยี่สิบสี่
25 var ref = createReference(สคริปต์[i].url, LoadCost);
26
27 ข้อมูลอ้างอิง กด (อ้างอิง);
28 }
29
30 scriptLoader.load (อ้างอิง, onComplete);
31}
32
33 ฟังก์ชั่น createReference(url, LoadCost)
34 {
35 var ref = วัตถุใหม่ ();
36 ref.url = url;
37 ref.onscriptload = "Jeffz.Sample.__onScriptLoad('" + url + "', " + LoadCost + ")";
38 กลับอ้างอิง;
39 }
40
41 ฟังก์ชัน onComplete()
42 {
43 Jeffz.Sample.__onScriptLoad = null;
44}
45
46 ฟังก์ชั่น onScriptLoad (url, LoadCost)
47 {
ความคืบหน้า 48 รายการ = 100.0 * ต้นทุนโหลด / ต้นทุนรวม;
49 document.getElementById("bar").style.width = ความคืบหน้า + "%";
50 document.getElementById("ข้อความ").innerHTML += ("<strong>" + url + "</strong>" + " โหลดแล้ว<br />");
51 }
52}
อนิจจาดูเหมือนว่าไม่จำเป็นต้องอธิบายโค้ดเลย จนถึงขณะนี้ แถบความคืบหน้าในการโหลดสคริปต์แบบธรรมดาได้เสร็จสมบูรณ์แล้ว ซึ่งค่อนข้างเรียบง่าย สามารถดาวน์โหลดโค้ดได้โดยคลิกที่นี่ หรือดูเอฟเฟกต์ได้โดยคลิกที่นี่
แต่นั่นคือจุดสิ้นสุดของเรื่องเหรอ? จริงๆ แล้ว ฉันไม่พอใจกับโซลูชันนี้มากนัก แม้ว่าควรจะเพียงพอสำหรับสถานการณ์ส่วนใหญ่ก็ตาม คุณจะสังเกตได้ว่าฉันใช้ Jeffz.Sample.LoadScripts เป็น Singleton กล่าวคือ ไม่มีอินสแตนซ์อื่นที่คล้ายคลึงกัน และที่จุดเริ่มต้นของวิธีโหลด จะมีการตัดสินว่ากำลังโหลดอยู่หรือไม่ หากเป็นเช่นนั้น ข้อยกเว้นจะถูกส่งออกไป เหตุผลโดยตรงในการบรรลุผลการโหลดแบบ "เธรดเดียว" ก็คือ มันถูกจำกัดโดยการใช้งาน Sys.ScriptLoader
โปรดดูที่บรรทัดที่ 44 ของโค้ด Sys.ScriptLoader โดยจะใช้ eval เพื่อโทรกลับ "ชั่วร้าย" เมื่อโหลดสคริปต์ นี่เป็นการใช้งานที่ไม่สะดวกนักสำหรับนักพัฒนา เนื่องจาก eval จึงเป็นไปไม่ได้ที่จะส่งการอ้างอิงไปยังฟังก์ชันเป็นฟังก์ชันเรียกกลับ สิ่งเดียวที่สามารถทำได้คือส่ง "รหัสรูท" ไปยัง Sys.ScriptLoader เป็นสตริง แม้ว่าจะยังคงเป็นไปได้ที่จะบรรลุการโหลดสคริปต์ "พร้อมกัน" ผ่าน Sys.ScriptLoader (พูดตรงๆ ก็คือ คุณสามารถสร้างคิวอย่าง Sys.ScriptLoader ได้มากที่สุด) จำนวนโค้ดจะเพิ่มขึ้นตามธรรมชาติ และความซับซ้อนของการพัฒนาก็จะเพิ่มขึ้นเช่นกัน เพิ่มขึ้น.
อย่างไรก็ตาม ฉันคิดว่าการโหลดสคริปต์แบบ "เธรดเดียว" นี้เพียงพอสำหรับสถานการณ์ส่วนใหญ่ และหากมีข้อกำหนด "พิเศษ" จริงๆ มันไม่ใช่เรื่องง่ายสำหรับนักพัฒนาส่วนใหญ่จะเขียนข้อกำหนดใหม่โดยอ้างอิงถึงตัวอย่างที่ชัดเจนของ Sys.ScriptLoader หรือไม่
http://www.cnblogs.com/JeffreyZhao/archive/2006/09/13/502357.html