Alguien me preguntó hoy cómo implementar una barra de progreso de carga de Javascript como el buzón 163.
No lo sé, pero no es difícil implementar uno, porque <script /> tiene onload y onreadystatechange. Además, tenemos Atlas.
Hay una clase en Atlas: Sys.ScriptLoader, su función es cargar varios archivos de script en la página en secuencia. Antes de implementarlo, analicemos el código de esta clase.
1Sys.ScriptLoader = función() {
2
3 //Matriz de objetos de referencia para todos los Scripts.
4 var _referencias;
5 //Función de devolución de llamada ejecutada después de cargar todos los scripts.
6 var _completionCallback;
7 // El contexto (parámetros) proporcionado al ejecutar la función de devolución de llamada.
8 var _callbackContext;
9
10 // Elemento HTTP (<script />) del script que se está cargando actualmente.
11 var _currentLoadingReference;
12 // La función de devolución de llamada llamada después de cargar el script actual.
13 var _currentOnScriptLoad;
14
15 // El único método de ScriptLoader es pasar tres parámetros. El significado de los parámetros no se repetirá.
16 this.load = función (referencias, finalizaciónCallback, callbackContext) {
17 _referencias = referencias;
18 _completionCallback = finalizaciónCallback;
19 _callbackContext = callbackContext;
20
21 cargarReferencias();
Veintidós }
veintitrés
24 // Comience a cargar referencias.
25 función cargarReferencias() {
26 // Si se está cargando un script actualmente.
27 // Esto significa que este método no se llama por primera vez, sino que se carga en un Script
28 // Llamado después de finalizar para cargar el siguiente script.
29 si (_currentLoadingReference) {
30 // Verifique el estado listo del elemento Script actual, que está completo en IE.
31 // Se cargan otros navegadores como FF (FF en realidad no tiene este atributo.
32 // Pero el siguiente código lo configurará como cargado).
33 // Si falla la carga, sal.
34 si ((_currentLoadingReference.readyState! = 'cargado') &&
35 (_currentLoadingReference.readyState! = 'completo')) {
36 regresan;
37 }
38 más {
39 // Entrar en esta rama indica una carga exitosa.
40
41 // Si el Script actual define la función onLoad.
42 si (_currentOnScriptLoad) {
43 // Llamado vía eval (aquí está el problema).
44 eval(_currentOnScriptLoad);
45 //Establecer en nulo para liberar recursos.
46 _currentOnScriptLoad = nulo;
47 }
48
49 // Establece los eventos relacionados en nulo para garantizar que se liberen los recursos.
50 si (Sys.Runtime.get_hostType()! = Sys.HostType.InternetExplorer) {
51 // Si el navegador actual no es IE, consulte el código a continuación
52 // Encontrarás que el evento de carga está definido para <script />.
53 _currentLoadingReference.onload = nulo;
54 }
55 más {
56 // Si es IE, mira el código a continuación y encontrarás que
57 // <script /> define el evento onreadystatechange.
58 _currentLoadingReference.onreadystatechange = nulo;
59 }
60
61 //Finalmente libera la referencia <script /> actual.
62 _currentLoadingReference = nulo;
63}
64}
65
66 // Si aún quedan Scripts descargados.
67 si (_referencias.longitud) {
68 // Quitar de la cola.
69 var referencia = _references.dequeue();
70 // Crear <script/>
71 var scriptElement = document.createElement('script');
72 //Establezca el <script /> actual y la función de devolución de llamada actual para una carga exitosa.
73 _currentLoadingReference = scriptElement;
74 _currentOnScriptLoad = referencia.onscriptload;
75
76 si (Sys.Runtime.get_hostType()! = Sys.HostType.InternetExplorer) {
77 // Si no es IE, establezca el atributo readyState para <script />.
78 // Y usa el evento onload.
79 scriptElement.readyState = 'cargado';
80 scriptElement.onload = cargarReferencias;
81 }
82 más {
83 // Si es IE, use el evento onreadystatechange.
84 scriptElement.onreadystatechange = loadReferences;
85}
86 scriptElement.type = 'texto/javascript';
87 scriptElement.src = referencia.url;
88
89 // Agregar <script /> al DOM
90 var headElement = document.getElementsByTagName('head')[0];
91 headElement.appendChild(scriptElement);
92
93 regresan;
94}
95
96 // Si la ejecución llega a este punto, significa que se han cargado todos los scripts.
97 // Si se define la función de devolución de llamada que se ejecuta después de cargar todos los scripts,
98 // Luego ejecuta y libera recursos.
99 si (_completionCallback) {
100 var completeCallback = _completionCallback;
101 var callbackContext = _callbackContext;
102
103 _completionCallback = nulo;
104 _callbackContext = nulo;
105
106 finalizaciónCallback(callbackContext);
107 }
108
109 _referencias = nulo;
110 }
111}
112Sys.ScriptLoader.registerClass('Sys.ScriptLoader');
Se puede ver que el método para que Sys.ScriptLoader cargue el script es agregar elementos <script /> a <header /> secuencialmente a través del código. De hecho, se utiliza muy raramente en Atlas.
De hecho, el código de Sys.ScriptLoader es muy simple y los comentarios que agregué parecen superfluos. Vale la pena señalar que todos los recursos se liberan tanto como sea posible. Preste especial atención al código que comienza en la línea 99. El cuerpo if primero usa variables temporales para retener dos variables globales y luego libera las variables globales. Su propósito es evitar pérdidas de memoria causadas por excepciones lanzadas cuando se ejecuta completeCallback, incluso si solo existe una posibilidad entre diez mil. Cuanto más Javascript haya, más fácil será provocar pérdidas de memoria. Es mejor prestar atención a este problema al escribir código JS.
A continuación, explique el primer parámetro del método de carga, referencias. Originalmente pensé que se trataba de una matriz de la clase Sys.Reference, pero descubrí que en realidad era bastante diferente. De todos modos, eche un vistazo al código de esta clase.
1Sys.Referencia = función() {
2
3 var _componente;
4 var _cargar;
5
6 this.get_component = función() {
7 retorno _componente;
8}
9 this.set_component = función (valor) {
10 _componente = valor;
11 }
12
13 this.get_onscriptload = función() {
14 retorno _onload;
15}
16 this.set_onscriptload = función (valor) {
17 _onload = valor;
18}
19
20 this.dispose = función() {
21 _componente = nulo;
Veintidós }
veintitrés
24 this.getDescriptor = función() {
25 var td = nuevo Sys.TypeDescriptor();
26
27 td.addProperty('componente', Objeto);
28 td.addProperty('onscriptload', Cadena);
29 regreso td;
30}
31}
32Sys.Reference.registerSealedClass('Sys.Reference', nulo, Sys.ITypeDescriptorProvider, Sys.IDisposable);
33Sys.TypeDescriptor.addType('script', 'referencia', Sys.Reference);
Al prestar atención al código de la clase Sys.ScriptLoader, podemos ver que cada elemento de la matriz de referencia es en realidad un simple "{ url : " http://www.sample.com/sample.js ", onscriptload : " alerta(1)"}" objeto de formulario. Pero esto está bien, puedes usar JSON fácilmente para construir dicha matriz.
En este punto, creo que todos deberían haber pensado en cómo usar Sys.ScriptLoader para crear fácilmente una barra de progreso de carga JS. Pero ahora que lo he escrito aquí, continuaré implementándolo de manera sencilla.
Primero está el archivo aspx.
1<%@ Idioma de página="C#" %>
2
3<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transicional//EN" " http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd ">
4
5<script runat="servidor">
6
7</script>
8
9<html xmlns=" http://www.w3.org/1999/xhtml " >
10<cabeza runat="servidor">
11 <title>Cargar scripts</title>
12 <lenguaje de escritura="javascript">
13 función Carga()
14 {
15 document.getElementById("bar").style.width = "0px";
16 scripts var = nueva matriz();
17 para (var i = 0; i < 8; i++)
18 {
19 var s = nuevo Objeto();
20 var dormir = Math.round((Math.random() * 400)) + 100;
21 s.url = "Script.ashx?sleep=" + dormir + "&t=" + Math.random();
22 s.costo = dormir;
23 scripts.push(s);
veinticuatro}
25
26 Jeffz.Sample.LoadScripts.cargar(scripts);
27}
28 </script>
29</head>
30<body style="font-family: Arial;">
31 <formulario id="form1" runat="servidor">
32 <div>
33 <atlas:ScriptManager ID="ScriptManager1" runat="servidor">
34 <Guiones>
35 <atlas:ScriptReference Path="js/LoadScripts.js" />
36 </Guiones>
37 </atlas:ScriptManager>
38
39 Barra de progreso:
40 <div style="borde: sólido 1px negro;">
41 <div id="bar" style="alto: 20px; ancho:0%; color de fondo:rojo;"></div>
42 </div>
43 <tipo de entrada="botón" onclick="Cargar()" valor="Cargar" />
44 <div id="mensaje"></div>
45 </div>
46 </formulario>
47</cuerpo>
48</html>
Muy sencillo. La barra de progreso más simple se crea utilizando dos DIV. La función Load() se llama cuando se hace clic en el botón. Esta función genera aleatoriamente enlaces de secuencias de comandos y genera una matriz de secuencias de comandos de 8 elementos. El formato de la matriz de scripts es el siguiente:
1var scripts =
2[
3 {url: " http://www.sample.com/sample1.js ", costo: costOfLoading1},
4 {url: " http://www.sample.com/sample2.js ", costo: costOfLoading2},
5 {url: " http://www.sample.com/sample3.js ", costo: costOfLoading3}
6];
No hace falta decir que el atributo URL de cada elemento y la función del costo es representar el valor del tiempo dedicado a cargar el archivo. Este valor no tiene unidad, sólo se utiliza la proporción de este valor en el consumo total. Además, puede ver que hay un Script.ashx, que se utiliza para simular una carga de script larga. Suspenderá el hilo durante un período de tiempo según el valor de suspensión en la cadena de consulta (como en la siguiente t). el propósito es simplemente evitar hacer clic en el botón cambiando la caché del navegador de la cadena de consulta), este archivo casi no tiene código y su implementación se puede ver en la descarga de muestra. Finalmente, se carga llamando al método Jeffz.Sample.LoadScripts.load, que implica el siguiente código, LoadScripts.js:
1Type.registerNamespace('Jeffz.Sample');
2
3Jeffz.Sample.LoadScripts = nueva función()
4{
5 var Costo total = 0;
6 var scriptLoader = nuevo Sys.ScriptLoader();
7
8 this.load = función (scripts)
9 {
10 si (Jeffz.Sample.__onScriptLoad! = nulo)
11 {
12 arrojar un nuevo error ("En progreso");
13}
14
15 Costo total = 0;
16 Jeffz.Sample.__onScriptLoad = onScriptLoad;
17 referencias var = nueva matriz();
18
19 var costo cargado = 0;
20 para (var i = 0; i < scripts.length; i++)
veintiuno {
22 costo total += scripts[i].costo;
23 costo cargado += scripts[i].costo;
veinticuatro
25 var ref = createReference(scripts[i].url, loadCost);
26
27 referencias.push(ref);
28 }
29
30 scriptLoader.load (referencias, onComplete);
31}
32
33 función crearReferencia (url, costo cargado)
34 {
35 var ref = nuevo Objeto();
36 ref.url = URL;
37 ref.onscriptload = "Jeffz.Sample.__onScriptLoad('" + URL + "', " + LoadedCost + ")";
38 árbitro de devolución;
39 }
40
41 función completada()
42 {
43 Jeffz.Sample.__onScriptLoad = nulo;
44}
45
46 función onScriptLoad (url, costo cargado)
47 {
48 var progreso = 100.0 * costo cargado / costo total;
49 document.getElementById("bar").style.width = progreso + "%";
50 document.getElementById("message").innerHTML += ("<strong>" + url + "</strong>" + " cargado.<br />");
51 }
52}
Por desgracia, parece que no es necesario explicar el código en absoluto. Hasta ahora, se ha completado una barra de progreso de carga de script simple, que es bastante simple. El código se puede descargar haciendo clic aquí, o el efecto se puede ver haciendo clic aquí.
¿Pero es ese el fin del asunto? De hecho, no estoy muy satisfecho con esta solución, aunque debería ser suficiente para la mayoría de situaciones. Puedes notar que implementé Jeffz.Sample.LoadScripts como Singleton, es decir, no existe otra instancia como esta. Y al comienzo del método de carga, se juzga si se está cargando. Si es así, se generará una excepción. La razón directa para lograr una carga de "un solo subproceso" es que está limitada por la implementación de Sys.ScriptLoader.
Mire la línea 44 del código Sys.ScriptLoader. Utiliza eval para devolver la llamada "malvada" cuando se carga el script. En realidad, esta es una implementación muy incómoda para los desarrolladores porque debido a eval, es imposible pasar una referencia a una función como una función de devolución de llamada. Lo único que se puede hacer es pasar el "código raíz" a Sys.ScriptLoader como una cadena. Aunque todavía es posible lograr una carga de scripts "concurrente" a través de Sys.ScriptLoader (para decirlo sin rodeos, como máximo puede crear una cola como Sys.ScriptLoader), la cantidad de código aumentará naturalmente y la complejidad del desarrollo también aumentará. aumentar.
Sin embargo, creo que esta carga de script de "un solo subproceso" es suficiente para la mayoría de las situaciones. Y si existen requisitos realmente "especiales", ¿no sería fácil para la mayoría de los desarrolladores reescribir uno refiriéndose a un ejemplo tan claro de Sys.ScriptLoader?
http://www.cnblogs.com/JeffreyZhao/archive/2006/09/13/502357.html