Jemand hat mich heute gefragt, wie man einen Javascript-Ladefortschrittsbalken wie das 163-Postfach implementiert.
Ich weiß es nicht, aber es ist nicht schwer, eines zu implementieren, da <script /> onload und onreadystatechange hat. Außerdem haben wir Atlas.
Es gibt eine Klasse in Atlas: Sys.ScriptLoader, deren Funktion darin besteht, mehrere Skriptdateien nacheinander auf die Seite zu laden. Bevor wir es implementieren, analysieren wir den Code dieser Klasse.
1Sys.ScriptLoader = function() {
2
3 //Array von Referenzobjekten für alle Skripte.
4 var _references;
5 //Callback-Funktion wird ausgeführt, nachdem alle Skripte geladen wurden.
6 var _completionCallback;
7 // Der Kontext (Parameter), der beim Ausführen der Rückruffunktion bereitgestellt wird.
8 var _callbackContext;
9
10 // HTTP-Element (<script />) des aktuell geladenen Skripts.
11 var _currentLoadingReference;
12 // Die Callback-Funktion, die aufgerufen wird, nachdem das aktuelle Skript geladen wurde.
13 var _currentOnScriptLoad;
14
15 // Die einzige Methode von ScriptLoader besteht darin, drei Parameter zu übergeben. Die Bedeutung der Parameter wird nicht wiederholt.
16 this.load = function(references, completionCallback, callbackContext) {
17 _references = Referenzen;
18 _completionCallback = abgeschlossenCallback;
19 _callbackContext = callbackContext;
20
21 LoadReferences();
zweiundzwanzig }
dreiundzwanzig
24 // Beginnen Sie mit dem Laden von Referenzen.
25 Funktion LoadReferences() {
26 // Wenn gerade ein Skript geladen wird.
27 // Dies bedeutet, dass diese Methode nicht zum ersten Mal aufgerufen, sondern in einem Skript geladen wird
28 // Wird nach Abschluss aufgerufen, um das nächste Skript zu laden.
29 if (_currentLoadingReference) {
30 // Überprüfen Sie den readyState des aktuellen Script-Elements, das unter IE vollständig ist.
31 // Andere Browser wie FF werden geladen (FF hat dieses Attribut eigentlich nicht.
32 // Aber der Code unten setzt es auf „geladen“).
33 // Wenn das Laden fehlschlägt, beenden.
34 if ((_currentLoadingReference.readyState != 'loaded') &&
35 (_currentLoadingReference.readyState != 'complete')) {
36 Rückkehr;
37 }
38 sonst {
39 // Das Betreten dieses Zweigs zeigt ein erfolgreiches Laden an.
40
41 // Wenn das aktuelle Skript die onLoad-Funktion definiert.
42 if (_currentOnScriptLoad) {
43 // Über Eval aufgerufen (hier liegt das Problem).
44 eval(_currentOnScriptLoad);
45 //Auf null setzen, um Ressourcen freizugeben.
46 _currentOnScriptLoad = null;
47 }
48
49 // Zugehörige Ereignisse auf null setzen, um sicherzustellen, dass Ressourcen freigegeben werden.
50 if (Sys.Runtime.get_hostType() != Sys.HostType.InternetExplorer) {
51 // Wenn der aktuelle Browser nicht IE ist, sehen Sie sich den folgenden Code an
52 // Sie werden feststellen, dass das Onload-Ereignis für <script /> definiert ist.
53 _currentLoadingReference.onload = null;
54 }
55 sonst {
56 // Wenn es sich um IE handelt, sehen Sie sich den Code unten an und Sie werden ihn finden
57 // <script /> definiert das onreadystatechange-Ereignis.
58 _currentLoadingReference.onreadystatechange = null;
59 }
60
61 //Endlich die aktuelle <script />-Referenz freigeben.
62 _currentLoadingReference = null;
63}
64}
65
66 // Wenn noch entladene Skripte vorhanden sind.
67 if (_references.length) {
68 // Aus der Warteschlange entfernen.
69 var reference = _references.dequeue();
70 // <script /> erstellen
71 var scriptElement = document.createElement('script');
72 //Setzen Sie das aktuelle <script /> und die aktuelle Callback-Funktion für ein erfolgreiches Laden.
73 _currentLoadingReference = scriptElement;
74 _currentOnScriptLoad = reference.onscriptload;
75
76 if (Sys.Runtime.get_hostType() != Sys.HostType.InternetExplorer) {
77 // Wenn es nicht IE ist, dann setzen Sie das Attribut readyState für <script />.
78 // Und verwenden Sie das Onload-Ereignis.
79 scriptElement.readyState = 'geladen';
80 scriptElement.onload = loadReferences;
81 }
82 sonst {
83 // Wenn es sich um IE handelt, verwenden Sie das Ereignis onreadystatechange.
84 scriptElement.onreadystatechange = loadReferences;
85}
86 scriptElement.type = 'text/javascript';
87 scriptElement.src = reference.url;
88
89 // <script /> zum DOM hinzufügen
90 var headElement = document.getElementsByTagName('head')[0];
91 headElement.appendChild(scriptElement);
92
93 Rückkehr;
94}
95
96 // Wenn die Ausführung diesen Punkt erreicht, bedeutet dies, dass alle Skripte geladen wurden.
97 // Wenn die Callback-Funktion definiert ist, die nach dem Laden aller Skripte ausgeführt wird,
98 // Anschließend Ressourcen ausführen und freigeben.
99 if (_completionCallback) {
100 varCompletionCallback = _completionCallback;
101 var callbackContext = _callbackContext;
102
103 _completionCallback = null;
104 _callbackContext = null;
105
106 VervollständigungCallback(callbackContext);
107 }
108
109 _references = null;
110 }
111}
112Sys.ScriptLoader.registerClass('Sys.ScriptLoader');
Es ist ersichtlich, dass die Methode für Sys.ScriptLoader zum Laden von Skripten darin besteht, <script />-Elemente nacheinander durch Code zu <header /> hinzuzufügen. Tatsächlich wird es in Atlas sehr selten verwendet.
Tatsächlich ist der Code von Sys.ScriptLoader sehr einfach und die von mir hinzugefügten Kommentare scheinen überflüssig zu sein. Es ist zu beachten, dass alle Ressourcen so weit wie möglich freigegeben werden. Achten Sie besonders auf den Code ab Zeile 99. Der if-Körper verwendet zunächst temporäre Variablen, um zwei globale Variablen beizubehalten, und gibt dann die globalen Variablen frei. Sein Zweck besteht darin, Speicherlecks zu vermeiden, die durch Ausnahmen verursacht werden, die bei der Ausführung von „CompletionCallback“ ausgelöst werden, selbst wenn die Wahrscheinlichkeit nur bei eins zu zehntausend liegt. Je mehr Javascript vorhanden ist, desto leichter kann es zu Speicherverlusten kommen. Achten Sie am besten auf dieses Problem, wenn Sie JS-Code schreiben.
Erklären Sie als Nächstes den ersten Parameter der Lademethode „Referenzen“. Ursprünglich dachte ich, dass dies ein Array der Sys.Reference-Klasse sei, aber ich stellte fest, dass es tatsächlich ganz anders war. Schauen Sie sich auf jeden Fall den Code dieser Klasse an.
1Sys.Reference = function() {
2
3 var _component;
4 var _onload;
5
6 this.get_component = function() {
7 return _component;
8}
9 this.set_component = function(value) {
10 _component = Wert;
11 }
12
13 this.get_onscriptload = function() {
14 return _onload;
15}
16 this.set_onscriptload = function(value) {
17 _onload = Wert;
18}
19
20 this.dispose = function() {
21 _component = null;
zweiundzwanzig }
dreiundzwanzig
24 this.getDescriptor = function() {
25 var td = new Sys.TypeDescriptor();
26
27 td.addProperty('component', Object);
28 td.addProperty('onscriptload', String);
29 Rückkehr td;
30}
31}
32Sys.Reference.registerSealedClass('Sys.Reference', null, Sys.ITypeDescriptorProvider, Sys.IDisposable);
33Sys.TypeDescriptor.addType('script', 'reference', Sys.Reference);
Wenn wir uns den Code der Sys.ScriptLoader-Klasse ansehen, können wir sehen, dass jedes Element des Referenzarrays eigentlich nur ein einfaches „{ url : „ http://www.sample.com/sample.js „, onscriptload : „ ist. Alert(1)"}" Formularobjekt. Aber das ist in Ordnung, Sie können JSON problemlos verwenden, um ein solches Array zu erstellen.
An diesem Punkt hätte meiner Meinung nach jeder darüber nachdenken sollen, wie man mit Sys.ScriptLoader auf einfache Weise einen JS-Ladefortschrittsbalken erstellen kann. Aber nachdem ich es hier geschrieben habe, werde ich es weiterhin auf einfache Weise umsetzen.
Zuerst ist die ASPX-Datei.
1<%@ Page Language="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="server">
6
7</script>
8
9<html xmlns=" http://www.w3.org/1999/xhtml " >
10<head runat="server">
11 <title>Skripte laden</title>
12 <script language="javascript">
13 Funktion Load()
14 {
15 document.getElementById("bar").style.width = "0px";
16 var-Skripte = new Array();
17 für (var i = 0; i < 8; i++)
18 {
19 var s = new Object();
20 var sleep = Math.round((Math.random() * 400)) + 100;
21 s.url = "Script.ashx?sleep=" + sleep + "&t=" + Math.random();
22 s.cost = Schlaf;
23 scripts.push(s);
vierundzwanzig }
25
26 Jeffz.Sample.LoadScripts.load(scripts);
27}
28 </script>
29</head>
30<body style="font-family: Arial;">
31 <form id="form1" runat="server">
32 <div>
33 <atlas:ScriptManager ID="ScriptManager1" runat="server">
34 <Skripte>
35 <atlas:ScriptReference Path="js/LoadScripts.js" />
36 </Scripts>
37 </atlas:ScriptManager>
38
39 Fortschrittsbalken:
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="message"></div>
45 </div>
46 </form>
47</body>
48</html>
Ganz einfach. Der einfachste Fortschrittsbalken wird mit zwei DIVs erstellt. Die Load()-Funktion wird aufgerufen, wenn auf die Schaltfläche geklickt wird. Diese Funktion generiert zufällig Skript-Links und ein 8-Element-Skript-Array. Das Format des Skript-Arrays ist wie folgt:
1var scripts =
2[
3 {URL: „ http://www.sample.com/sample1.js “, Kosten: costOfLoading1},
4 {URL: „ http://www.sample.com/sample2.js “, Kosten: costOfLoading2},
5 {URL: „ http://www.sample.com/sample3.js “, Kosten: costOfLoading3}
6];
Es erübrigt sich zu erwähnen, dass die URL-Attribute jedes Elements und die Funktion der Kosten darin bestehen, den Wert der für das Laden der Datei aufgewendeten Zeit darzustellen. Dieser Wert hat keine Einheit, es wird nur der Anteil dieses Wertes am Gesamtverbrauch verwendet. Darüber hinaus können Sie sehen, dass es eine Script.ashx gibt, die zum Simulieren eines langen Skriptladens verwendet wird. Sie lässt den Thread für einen bestimmten Zeitraum schlafen, basierend auf dem Wert von „sleep“ im Abfragestring (wie für das folgende t). Der Zweck besteht nur darin, das Klicken auf die Schaltfläche zu vermeiden, indem der Browser-Cache der Abfragezeichenfolge geändert wird. Diese Datei enthält fast keinen Code und ihre Implementierung kann im Beispieldownload gesehen werden. Schließlich wird es durch Aufrufen der Methode Jeffz.Sample.LoadScripts.load geladen, die den folgenden Code LoadScripts.js umfasst:
1Type.registerNamespace('Jeffz.Sample');
2
3Jeffz.Sample.LoadScripts = neue Funktion()
4{
5 var totalCost = 0;
6 var scriptLoader = new Sys.ScriptLoader();
7
8 this.load = Funktion(Skripte)
9 {
10 if (Jeffz.Sample.__onScriptLoad != null)
11 {
12 throw new Error("In progress");
13}
14
15 Gesamtkosten = 0;
16 Jeffz.Sample.__onScriptLoad = onScriptLoad;
17 Var-Referenzen = new Array();
18
19 var geladenCost = 0;
20 für (var i = 0; i < scripts.length; i++)
einundzwanzig {
22 totalCost += scripts[i].cost;
23 geladenCost += scripts[i].cost;
vierundzwanzig
25 var ref = createReference(scripts[i].url, LoadedCost);
26
27 Referenzen.push(ref);
28 }
29
30 scriptLoader.load(references, onComplete);
31}
32
33 Funktion createReference(url, geladene Kosten)
34 {
35 var ref = new Object();
36 ref.url = url;
37 ref.onscriptload = "Jeffz.Sample.__onScriptLoad('" + url + "', " + LoadedCost + ")";
38 Return-Ref;
39 }
40
41 Funktion onComplete()
42 {
43 Jeffz.Sample.__onScriptLoad = null;
44}
45
46 Funktion onScriptLoad(url, geladene Kosten)
47 {
48 var progress = 100,0 * geladene Kosten / Gesamtkosten;
49 document.getElementById("bar").style.width = progress + "%";
50 document.getElementById("message").innerHTML += ("<strong>" + url + "</strong>" + " geladen.<br />");
51 }
52}
Leider scheint es überhaupt nicht nötig zu sein, den Code zu erklären. Bisher wurde ein einfacher Fortschrittsbalken zum Laden des Skripts erstellt, was recht einfach ist. Der Code kann heruntergeladen werden, indem Sie hier klicken, oder der Effekt kann angezeigt werden, indem Sie hier klicken.
Aber ist die Sache damit erledigt? Tatsächlich bin ich mit dieser Lösung nicht sehr zufrieden, obwohl sie für die meisten Situationen ausreichen sollte. Sie können feststellen, dass ich Jeffz.Sample.LoadScripts als Singleton implementiert habe, das heißt, es gibt keine vergleichbare Instanz. Und zu Beginn der Lademethode wird beurteilt, ob sie geladen wird. Wenn ja, wird eine Ausnahme ausgelöst. Der direkte Grund für das Erreichen eines solchen „Single-Threaded“-Ladens besteht darin, dass es durch die Implementierung von Sys.ScriptLoader begrenzt ist.
Bitte schauen Sie sich Zeile 44 des Sys.ScriptLoader-Codes an. Dort wird eval verwendet, um einen Rückruf durchzuführen, wenn das Skript geladen wird. Dies ist tatsächlich eine sehr unbequeme Implementierung für Entwickler, da es aufgrund von eval unmöglich ist, einen Verweis auf eine Funktion als Callback-Funktion zu übergeben. Das einzige, was getan werden kann, ist, den „Root-Code“ als String an Sys.ScriptLoader zu übergeben. Obwohl es immer noch möglich ist, ein „gleichzeitiges“ Laden von Skripten über Sys.ScriptLoader zu erreichen (um es ganz klar auszudrücken, können Sie höchstens eine Warteschlange wie Sys.ScriptLoader erstellen), wird die Menge an Code natürlich zunehmen, und auch die Komplexität der Entwicklung wird zunehmen Zunahme.
Ich denke jedoch, dass dieses „Single-Threaded“-Skriptladen für die meisten Situationen ausreichend ist. Und wenn es wirklich „spezielle“ Anforderungen gibt, wäre es dann für die Mehrheit der Entwickler nicht einfach, eine solche umzuschreiben, indem sie sich auf ein so klares Beispiel von Sys.ScriptLoader beziehen?
http://www.cnblogs.com/JeffreyZhao/archive/2006/09/13/502357.html