Alguém me perguntou hoje como implementar uma barra de progresso de carregamento de Javascript como a caixa de correio 163.
Não sei, mas não é difícil implementar um, pois <script /> possui onload e onreadystatechange. Além disso, temos Atlas.
Existe uma classe no Atlas: Sys.ScriptLoader, sua função é carregar vários arquivos Script na página em sequência. Antes de implementá-lo, vamos analisar o código desta classe.
1Sys.ScriptLoader = função() {
2
3 //Array de objetos de referência para todos os Scripts.
4 var _referências;
5 //Função de callback executada após todos os Scripts serem carregados.
6 var _completionCallback;
7 // O contexto (parâmetros) fornecido ao executar a função de retorno de chamada.
8 var _callbackContext;
9
10 // Elemento HTTP (<script />) do Script atualmente carregado.
11 var _currentLoadingReference;
12 // A função de retorno de chamada chamada após o script atual ser carregado.
13 var _currentOnScriptLoad;
14
15 // O único método do ScriptLoader é passar três parâmetros. O significado dos parâmetros não será repetido.
16 this.load = função (referências, conclusãoCallback, callbackContext) {
17 _referências = referências;
18 _completionCallback = completeCallback;
19 _callbackContext = callbackContext;
20
21 loadReferences();
vinte e dois }
vinte e três
24 // Começa a carregar referências.
25 função loadReferences() {
26 // Se um Script está sendo carregado no momento.
27 // Isso significa que este método não é chamado pela primeira vez, mas é carregado em um Script
28 // Chamado após a conclusão para carregar o próximo Script.
29 se (_currentLoadingReference) {
30 // Verifica o readyState do elemento Script atual, que está completo no IE.
31 // Outros navegadores como o FF são carregados (o FF na verdade não possui esse atributo.
32 // Mas o código abaixo irá configurá-lo para carregado).
33 // Se o carregamento falhar, saia.
34 if ((_currentLoadingReference.readyState! = 'carregado') &&
35 (_currentLoadingReference.readyState! = 'completo')) {
36 retorno;
37}
38 mais {
39 // Entrar neste branch indica carregamento bem-sucedido.
40
41 // Se o Script atual define a função onLoad.
42 se (_currentOnScriptLoad) {
43 // Chamado via eval (aqui está o problema).
44 avaliação(_currentOnScriptLoad);
45 //Definido como nulo para liberar recursos.
46 _currentOnScriptLoad = nulo;
47}
48
49 // Defina eventos relacionados como nulos para garantir que os recursos sejam liberados.
50 se (Sys.Runtime.get_hostType()! = Sys.HostType.InternetExplorer) {
51 // Se o navegador atual não for IE, veja o código abaixo
52 // Você descobrirá que o evento onload está definido para <script />.
53 _currentLoadingReference.onload = null;
54}
55 mais {
56 // Se for IE, veja o código abaixo e você descobrirá que
57 // <script /> define o evento onreadystatechange.
58 _currentLoadingReference.onreadystatechange = null;
59}
60
61 //Finalmente libera a referência <script /> atual.
62 _currentLoadingReference = nulo;
63}
64}
65
66 // Se ainda houver Scripts descarregados.
67 if (_referências.length) {
68 // Desfilar.
69 var referência = _references.dequeue();
70 // Cria <script />
71 var scriptElement = document.createElement('script');
72 //Defina o <script /> atual e a função de retorno de chamada atual para carregamento bem-sucedido.
73 _currentLoadingReference = scriptElement;
74 _currentOnScriptLoad = referência.onscriptload;
75
76 se (Sys.Runtime.get_hostType()! = Sys.HostType.InternetExplorer) {
77 // Se não for IE, então defina o atributo readyState para <script />.
78 // E use o evento onload.
79 scriptElement.readyState = 'carregado';
80 scriptElement.onload = loadReferences;
81}
82 mais {
83 // Se for IE, use o evento onreadystatechange.
84 scriptElement.onreadystatechange = loadReferences;
85}
86 scriptElement.type = 'texto/javascript';
87 scriptElement.src = referência.url;
88
89 // Adicione <script /> ao DOM
90 var headElement = document.getElementsByTagName('head')[0];
91 headElement.appendChild(scriptElement);
92
93 retorno;
94}
95
96 // Se a execução chegar a este ponto, significa que todos os scripts foram carregados.
97 // Se a função de retorno de chamada que é executada após o carregamento de todos os Scripts for definida,
98 // Em seguida, execute e libere recursos.
99 se (_completionCallback) {
100 var conclusãoCallback = _completionCallback;
101 var callbackContext = _callbackContext;
102
103 _completionCallback = null;
104 _callbackContext=nulo;
105
106 conclusãoCallback(callbackContext);
107}
108
109 _referências = nulo;
110}
111}
112Sys.ScriptLoader.registerClass('Sys.ScriptLoader');
Pode-se observar que o método para Sys.ScriptLoader carregar o script é adicionar elementos <script /> a <header /> sequencialmente por meio do código. Na verdade, é usado muito raramente no Atlas.
Na verdade, o código do Sys.ScriptLoader é muito simples e os comentários que adicionei parecem supérfluos. Vale ressaltar que todos os recursos são liberados na medida do possível. Preste atenção especial ao código a partir da linha 99. O corpo if primeiro usa variáveis temporárias para reter duas variáveis globais e depois libera as variáveis globais. Seu objetivo é evitar vazamentos de memória causados por exceções lançadas quando o completeCallback é executado, mesmo que haja apenas uma possibilidade em dez mil. Quanto mais Javascript houver, mais fácil será causar vazamentos de memória. É melhor prestar atenção a esse problema ao escrever código JS.
A seguir, explique o primeiro parâmetro do método de carregamento, referências. Originalmente pensei que fosse um array da classe Sys.Reference, mas descobri que na verdade era bem diferente. De qualquer forma, dê uma olhada no código desta classe.
1Sys.Referência = função() {
2
3 var _componente;
4 var _onload;
5
6 this.get_component = function() {
7 retornar _componente;
8}
9 this.set_component = função (valor) {
10 _componente = valor;
11}
12
13 this.get_onscriptload = function() {
14 return _onload;
15}
16 this.set_onscriptload = função (valor) {
17 _onload = valor;
18}
19
20 this.dispose = function() {
21 _componente = nulo;
vinte e dois }
vinte e três
24 this.getDescriptor = function() {
25 var td = new Sys.TypeDescriptor();
26
27 td.addProperty('componente', Objeto);
28 td.addProperty('onscriptload', String);
29 retorno td;
30}
31}
32Sys.Reference.registerSealedClass('Sys.Reference', null, Sys.ITypeDescriptorProvider, Sys.IDisposable);
33Sys.TypeDescriptor.addType('script', 'referência', Sys.Reference);
Prestando atenção ao código da classe Sys.ScriptLoader, podemos ver que cada elemento do array de referência é na verdade apenas um simples "{url:" http://www.sample.com/sample.js ", onscriptload:" alert(1)"}" objeto de formulário. Mas tudo bem, você pode facilmente usar JSON para construir tal array.
Neste ponto, acho que todos deveriam ter pensado em como usar Sys.ScriptLoader para criar facilmente uma barra de progresso de carregamento JS. Mas agora que escrevi aqui, continuarei a implementá-lo de forma simples.
O primeiro é o arquivo aspx.
1<%@ Idioma da página="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="servidor">
6
7</script>
8
9<html xmlns=" http://www.w3.org/1999/xhtml " >
10<head runat="servidor">
11 <title>Carregar scripts</title>
12 <linguagem script="javascript">
13 função Carregar()
14 {
15 document.getElementById("bar").style.width = "0px";
16 scripts var = new Array();
17 para (var i = 0; i < 8; i++)
18 {
19 vars = new Object();
20 var sleep = Math.round((Math.random() * 400)) + 100;
21 s.url = "Script.ashx?sleep=" + sleep + "&t=" + Math.random();
22 s.custo = sono;
23 scripts.push(s);
vinte e quatro }
25
26 Jeffz.Sample.LoadScripts.load(scripts);
27}
28
29</head>
30<body style="font-family: Arial;">
31 <form id="form1" runat="servidor">
32
33 <atlas:ScriptManager ID="ScriptManager1" runat="servidor">
34 <Roteiros>
35 <atlas:ScriptReference Path="js/LoadScripts.js" />
36 </Scripts>
37 </atlas:ScriptManager>
38
39 Barra de Progresso:
40 <div style="border: solid 1px preto;">
41 <div id="bar" style="height: 20px; width:0%; background-color:Red;"></div>
42
43 <input type="button" onclick="Load()" value="Load" />
44 <div id="mensagem"></div>
45
46 </form>
47</corpo>
48</html>
Muito simples. A barra de progresso mais simples é feita usando dois DIVs. A função Load() é chamada quando o botão é clicado. Esta função gera links de script aleatoriamente e gera uma matriz de scripts de 8 elementos. O formato da matriz de scripts é o seguinte:
1var scripts =
2[
3 {url: " http://www.sample.com/sample1.js ", custo: costOfLoading1 },
4 {url: " http://www.sample.com/sample2.js ", custo: costOfLoading2 },
5 {url: " http://www.sample.com/sample3.js ", custo: costOfLoading3 }
6];
Escusado será dizer que o atributo url de cada elemento e a função do custo é representar o valor do tempo gasto no carregamento do arquivo. Este valor não possui unidade, apenas é utilizada a proporção deste valor no consumo total. Além disso, você pode ver que existe um Script.ashx, que é usado para simular um longo carregamento de script. Ele suspenderá o thread por um período de tempo com base no valor de sleep na string de consulta (como no seguinte t, o objetivo é apenas evitar clicar no botão alterando o cache do navegador da querystring), este arquivo quase não possui código e sua implementação pode ser vista no download da amostra. Finalmente, ele é carregado chamando o método Jeffz.Sample.LoadScripts.load, que envolve o seguinte código, LoadScripts.js:
1Type.registerNamespace('Jeffz.Sample');
2
3Jeffz.Sample.LoadScripts = nova função()
4{
5 var CustoTotal = 0;
6 var scriptLoader = new Sys.ScriptLoader();
7
8 this.load = função (scripts)
9 {
10 se (Jeffz.Sample.__onScriptLoad!=nulo)
11 {
12 lançar novo Erro("Em andamento");
13}
14
15 Custo total = 0;
16 Jeffz.Sample.__onScriptLoad = onScriptLoad;
17 var referências = new Array();
18
19 var CustoCarregado = 0;
20 para (var i = 0; i < scripts.length; i++)
vinte e um {
22 totalCost += scripts[i].cost;
23 carregadoCost += scripts[i].cost;
vinte e quatro
25 var ref = createReference(scripts[i].url, LoadedCost);
26
27 referências.push(ref);
28}
29
30 scriptLoader.load(referências, onComplete);
31}
32
33 função createReference(url,loadedCost)
34 {
35 var ref = new Object();
36 ref.url = url;
37 ref.onscriptload = "Jeffz.Sample.__onScriptLoad('" + url + "', " + carregadoCost + ")";
38 referência de retorno;
39}
40
41 função onComplete()
42 {
43 Jeffz.Sample.__onScriptLoad = null;
44}
45
46 função onScriptLoad(url,loadedCost)
47 {
48 var progresso = 100,0 *loadCost/totalCost;
49 document.getElementById("bar").style.width = progresso + "%";
50 document.getElementById("message").innerHTML += ("<strong>" + url + "</strong>" + " carregado.<br />");
51}
52}
Infelizmente, parece que não há necessidade de explicar o código. Até agora, uma barra simples de progresso de carregamento de script foi concluída, o que é bastante simples. O código pode ser baixado clicando aqui, ou o efeito pode ser visualizado clicando aqui.
Mas isso é o fim da questão? Na verdade, não estou muito satisfeito com esta solução, embora deva ser suficiente para a maioria das situações. Você pode notar que implementei Jeffz.Sample.LoadScripts como um Singleton, ou seja, não existe outra instância igual. E no início do método de carregamento, é avaliado se ele está carregando. Em caso afirmativo, uma exceção será lançada. A razão direta para obter esse carregamento de "thread único" é que ele é limitado pela implementação de Sys.ScriptLoader.
Por favor, veja a linha 44 do código Sys.ScriptLoader. Ela usa eval para retornar a chamada "malvada" quando o script é carregado. Na verdade, esta é uma implementação muito desconfortável para os desenvolvedores, pois com eval é impossível passar uma referência a uma função como uma função de retorno de chamada. A única coisa que pode ser feita é passar o "código raiz" para Sys.ScriptLoader como uma string. Embora ainda seja possível obter carregamento de script "simultâneo" por meio de Sys.ScriptLoader (para ser franco, no máximo você pode criar uma fila como Sys.ScriptLoader), a quantidade de código aumentará naturalmente e a complexidade do desenvolvimento também aumentará. aumentar.
No entanto, acho que esse carregamento de script "single-threaded" é suficiente para a maioria das situações. E se houver requisitos realmente "especiais", não seria fácil para a maioria dos desenvolvedores reescrever um deles referindo-se a um exemplo tão claro de Sys.ScriptLoader?
http://www.cnblogs.com/JeffreyZhao/archive/2006/09/13/502357.html