Quelqu'un m'a demandé aujourd'hui comment implémenter une barre de progression de chargement Javascript comme la boîte aux lettres 163.
Je ne sais pas, mais ce n'est pas difficile d'en implémenter un, car <script /> a onload et onreadystatechange. Nous avons également Atlas.
Il existe une classe dans Atlas : Sys.ScriptLoader, sa fonction est de charger plusieurs fichiers Script dans la page en séquence. Avant de l’implémenter, analysons le code de cette classe.
1Sys.ScriptLoader = fonction() {
2
3 //Tableau d'objets de référence pour tous les scripts.
4 var _références ;
5 //Fonction de rappel exécutée une fois tous les scripts chargés.
6 variable _completionCallback ;
7 // Le contexte (paramètres) fourni lors de l'exécution de la fonction de rappel.
8 variable _callbackContext ;
9
10 // Élément HTTP (<script />) du script en cours de chargement.
11 var _currentLoadingReference ;
12 // La fonction de rappel appelée après le chargement du script actuel.
13 variable _currentOnScriptLoad ;
14
15 // La seule méthode de ScriptLoader est de passer trois paramètres. La signification des paramètres ne sera pas répétée.
16 this.load = function(références, complétionCallback, callbackContext) {
17 _références = références ;
18 _completionCallback = complétionCallback ;
19 _callbackContext = callbackContext;
20
21 LoadReferences();
vingt-deux }
vingt-trois
24 // Commencez à charger les références.
25 fonction loadReferences() {
26 // Si un script est en cours de chargement.
27 // Cela signifie que cette méthode n'est pas appelée pour la première fois, mais est chargée dans un Script
28 // Appelé une fois terminé pour charger le script suivant.
29 si (_currentLoadingReference) {
30 // Vérifiez le readyState de l'élément Script actuel, qui est complet sous IE.
31 // D'autres navigateurs tels que FF sont chargés (FF n'a en fait pas cet attribut.
32 // Mais le code ci-dessous le définira sur chargé).
33 // Si le chargement échoue, quittez.
34 si ((_currentLoadingReference.readyState != 'chargé') &&
35 (_currentLoadingReference.readyState != 'complete')) {
36 retour;
37 }
38 autres {
39 // La saisie de cette branche indique un chargement réussi.
40
41 // Si le Script actuel définit la fonction onLoad.
42 si (_currentOnScriptLoad) {
43 // Appelé via eval (voici le problème).
44 eval(_currentOnScriptLoad);
45 //Défini sur null pour libérer les ressources.
46 _currentOnScriptLoad = nul ;
47 }
48
49 // Définissez les événements associés sur null pour garantir la libération des ressources.
50 si (Sys.Runtime.get_hostType() != Sys.HostType.InternetExplorer) {
51 // Si le navigateur actuel n'est pas IE, voir le code ci-dessous
52 // Vous constaterez que l'événement onload est défini pour <script />.
53 _currentLoadingReference.onload = null ;
54 }
55 autres {
56 // S'il s'agit d'IE, consultez le code ci-dessous et vous constaterez que
57 // <script /> définit l'événement onreadystatechange.
58 _currentLoadingReference.onreadystatechange = null ;
59 }
60
61 //Publiez enfin la référence <script /> actuelle.
62 _currentLoadingReference = null ;
63}
64}
65
66 // S'il y a encore des Scripts déchargés.
67 si (_references.length) {
68 // Retirer la file d'attente.
69 référence var = _references.dequeue();
70 // Créer un <script />
71 var scriptElement = document.createElement('script');
72 //Définissez le <script /> actuel et la fonction de rappel actuelle pour un chargement réussi.
73 _currentLoadingReference = scriptElement ;
74 _currentOnScriptLoad = référence.onscriptload;
75
76 si (Sys.Runtime.get_hostType() != Sys.HostType.InternetExplorer) {
77 // S'il ne s'agit pas d'IE, définissez l'attribut readyState pour <script />.
78 // Et utilisez l'événement onload.
79 scriptElement.readyState = 'chargé';
80 scriptElement.onload = loadReferences;
81 }
82 autre {
83 // S'il s'agit d'IE, utilisez l'événement onreadystatechange.
84 scriptElement.onreadystatechange = loadReferences;
85}
86 scriptElement.type = 'texte/javascript';
87 scriptElement.src = référence.url;
88
89 // Ajouter <script /> au DOM
90 var headElement = document.getElementsByTagName('head')(0];
91 headElement.appendChild(scriptElement);
92
93 retour ;
94}
95
96 // Si l'exécution atteint ce point, cela signifie que tous les scripts ont été chargés.
97 // Si la fonction de rappel qui est exécutée après le chargement de tous les scripts est définie,
98 // Puis exécutez et libérez les ressources.
99 si (_completionCallback) {
100 varcompleteCallback = _completionCallback;
101 var callbackContext = _callbackContext;
102
103 _completionCallback = nul ;
104 _callbackContext = nul ;
105
106 complétionCallback(callbackContext);
107 }
108
109 _références = nul ;
110 }
111}
112Sys.ScriptLoader.registerClass('Sys.ScriptLoader');
On peut voir que la méthode utilisée par Sys.ScriptLoader pour charger le script consiste à ajouter des éléments <script /> à <header /> de manière séquentielle via le code. En fait, il est très rarement utilisé dans Atlas.
En fait, le code de Sys.ScriptLoader est très simple, et les commentaires que j'ai ajoutés semblent superflus. Il convient de noter que toutes les ressources sont libérées autant que possible. Portez une attention particulière au code à partir de la ligne 99. Le corps if utilise d'abord des variables temporaires pour conserver deux variables globales, puis libère les variables globales. Son objectif est d'éviter les fuites de mémoire causées par les exceptions levées lors de l'exécution de CompletCallback, même s'il n'y a qu'une possibilité sur dix mille. Plus il y a de Javascript, plus il est facile de provoquer des fuites de mémoire. Il est préférable de prêter attention à ce problème lors de l'écriture du code JS.
Ensuite, expliquez le premier paramètre de la méthode de chargement, les références. Au départ, je pensais qu'il s'agissait d'un tableau de la classe Sys.Reference, mais j'ai trouvé que c'était en fait assez différent. Quoi qu'il en soit, jetez un œil au code de cette classe.
1Sys.Référence = fonction() {
2
3 var_composant ;
4 variables _onload ;
5
6 this.get_component = fonction() {
7 retourner _component ;
8}
9 this.set_component = fonction (valeur) {
10 _composant = valeur ;
11 }
12
13 this.get_onscriptload = fonction() {
14 retour _onload ;
15}
16 this.set_onscriptload = fonction (valeur) {
17 _onload = valeur ;
18}
19
20 this.dispose = fonction() {
21 _composant = nul ;
vingt-deux }
vingt-trois
24 this.getDescriptor = fonction() {
25 var td = new Sys.TypeDescriptor();
26
27 td.addProperty('composant', Objet);
28 td.addProperty('onscriptload', String);
29 retour td;
30}
31}
32Sys.Reference.registerSealedClass('Sys.Reference', null, Sys.ITypeDescriptorProvider, Sys.IDisposable);
33Sys.TypeDescriptor.addType('script', 'référence', Sys.Reference);
En prêtant attention au code de la classe Sys.ScriptLoader, nous pouvons voir que chaque élément du tableau de référence n'est en réalité qu'un simple "{ url : " http://www.sample.com/sample.js ", onscriptload : " alert(1)"}" objet de formulaire. Mais ce n'est pas grave, vous pouvez facilement utiliser JSON pour construire un tel tableau.
À ce stade, je pense que tout le monde aurait dû réfléchir à la façon d'utiliser Sys.ScriptLoader pour créer facilement une barre de progression de chargement JS. Mais maintenant que je l’ai écrit ici, je vais continuer à le mettre en œuvre de manière simple.
Le premier est le fichier aspx.
1<%@ Langage de la page="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="serveur">
6
7</script>
8
9<html xmlns=" http://www.w3.org/1999/xhtml " >
10<head runat="serveur">
11 <title>Charger des scripts</title>
12 <langage de script="javascript">
13 fonctions Charger()
14 {
15 document.getElementById("bar").style.width = "0px";
16 scripts var = new Array();
17 pour (var i = 0; i < 8; i++)
18 {
19 var s = nouvel objet ();
20 var sommeil = Math.round((Math.random() * 400)) + 100;
21 s.url = "Script.ashx?sleep=" + sommeil + "&t=" + Math.random();
22 s.coût = dormir;
23 scripts.push(s);
vingt-quatre }
25
26 Jeffz.Sample.LoadScripts.load(scripts);
27}
28 </script>
29
30<body style="font-family: Arial;">
31 <form id="form1" runat="serveur">
32 <div>
33 <atlas:ScriptManager ID="ScriptManager1" runat="server">
34 <Scripts>
35 <atlas:ScriptReference Path="js/LoadScripts.js" />
36 </Scripts>
37 </atlas:ScriptManager>
38
39 Barre de progression :
40 <div style="border: solid 1px black;">
41 <div id="bar" style="hauteur : 20px; largeur :0%; couleur d'arrière-plan :Rouge;"></div>
42 </div>
43 <input type="button" onclick="Load()" value="Load" />
44 <div id="message"></div>
45 </div>
46 </form>
47
48</html>
Très simple. La barre de progression la plus simple est réalisée à l'aide de deux DIV. La fonction Load() est appelée lorsque le bouton est cliqué. Cette fonction génère aléatoirement des liens de script et génère un tableau de scripts à 8 éléments. Le format du tableau scripts est le suivant :
1var scripts =
2[
3 { url : " http://www.sample.com/sample1.js ", coût : costOfLoading1 },
4 { url : " http://www.sample.com/sample2.js ", coût : costOfLoading2 },
5 { url : " http://www.sample.com/sample3.js ", coût : costOfLoading3 }
6];
Inutile de dire que l'attribut url de chaque élément et la fonction de coût sont de représenter la valeur du temps passé à charger le fichier. Cette valeur n'a pas d'unité, seule la proportion de cette valeur dans la consommation totale est utilisée. De plus, vous pouvez voir qu'il existe un Script.ashx, qui est utilisé pour simuler un long chargement de script. Il mettra le thread en veille pendant un certain temps en fonction de la valeur de sleep dans la chaîne de requête (comme pour le t suivant). le but est juste d'éviter de cliquer sur le bouton en modifiant le cache du navigateur de chaîne de requête), ce fichier n'a presque pas de code et son implémentation est visible dans l'exemple de téléchargement. Enfin, il est chargé en appelant la méthode Jeffz.Sample.LoadScripts.load, qui implique le code suivant, LoadScripts.js :
1Type.registerNamespace('Jeffz.Sample');
2
3Jeffz.Sample.LoadScripts = nouvelle fonction()
4{
5 var coût total = 0 ;
6 var scriptLoader = new Sys.ScriptLoader();
7
8 this.load = fonction (scripts)
9 {
10 si (Jeffz.Sample.__onScriptLoad != null)
11 {
12 throw new Error("En cours");
13}
14
15 Coût total = 0 ;
16 Jeffz.Sample.__onScriptLoad = onScriptLoad;
17 références var = new Array();
18
19 variables LoadedCost = 0 ;
20 pour (var i = 0; i < scripts.length; i++)
vingt-et-un {
22 coût total += scripts[i].cost ;
23 chargéCost += scripts[i].cost ;
vingt-quatre
25 var ref = createReference(scripts[i].url,loadedCost);
26
27 références.push(réf);
28 }
29
30 scriptLoader.load(références, onComplete);
31}
32
33 fonction createReference(url,loadedCost)
34 {
35 var ref = new Object();
36 ref.url = url;
37 ref.onscriptload = "Jeffz.Sample.__onScriptLoad('" + url + "', " +loadedCost + ")";
38 référence de retour ;
39 }
40
41 fonction onComplete()
42 {
43 Jeffz.Sample.__onScriptLoad = null ;
44}
45
46 fonction onScriptLoad(url,loadCost)
47 {
48 var progress = 100,0 *loadedCost / totalCost ;
49 document.getElementById("bar").style.width = progress + "%";
50 document.getElementById("message").innerHTML += ("<strong>" + url + "</strong>" + " chargé.<br />");
51 }
52}
Hélas, il semble qu'il ne soit pas du tout nécessaire d'expliquer le code. Jusqu'à présent, une simple barre de progression du chargement du script a été complétée, ce qui est assez simple. Le code peut être téléchargé en cliquant ici, ou l'effet peut être consulté en cliquant ici.
Mais est-ce que c’est la fin de l’affaire ? En fait, je ne suis pas très satisfait de cette solution, même si elle devrait suffire dans la plupart des situations. Vous pouvez remarquer que j'ai implémenté Jeffz.Sample.LoadScripts en tant que Singleton, c'est-à-dire qu'il n'existe aucune autre instance comparable. Et au début de la méthode de chargement, il est jugé si elle est en cours de chargement. Si tel est le cas, une exception sera levée. La raison directe pour réaliser un tel chargement « monothread » est qu'il est limité par l'implémentation de Sys.ScriptLoader.
Veuillez regarder la ligne 44 du code Sys.ScriptLoader. Il utilise eval pour rappeler "méchant" lorsque le script est chargé. Il s'agit en fait d'une implémentation très inconfortable pour les développeurs, car à cause d'eval, il est impossible de passer une référence à une fonction en tant que fonction de rappel. La seule chose qui peut être faite est de transmettre le « code racine » à Sys.ScriptLoader sous forme de chaîne. Bien qu'il soit toujours possible de réaliser un chargement de script « simultané » via Sys.ScriptLoader (pour parler franchement, vous pouvez tout au plus créer une file d'attente comme Sys.ScriptLoader), la quantité de code augmentera naturellement et la complexité du développement augmentera également. augmenter.
Cependant, je pense que ce chargement de script "mono-thread" est suffisant pour la plupart des situations. Et s'il y a des exigences vraiment « particulières », ne serait-il pas facile pour la majorité des développeurs d'en réécrire une en se référant à un exemple aussi clair de Sys.ScriptLoader ?
http://www.cnblogs.com/JeffreyZhao/archive/2006/09/13/502357.html