Кто-то спросил меня сегодня, как реализовать индикатор выполнения загрузки Javascript, например, почтовый ящик 163.
Не знаю, но реализовать его несложно, потому что в <script /> есть onload и onreadystatechange. Также у нас есть Атлас.
В Atlas есть класс: Sys.ScriptLoader, его функция — последовательно загружать на страницу несколько файлов сценариев. Прежде чем реализовать, давайте проанализируем код этого класса.
1Sys.ScriptLoader = функция() {
2
3 //Массив ссылочных объектов для всех скриптов.
4 вар _references;
5 //Функция обратного вызова, выполняемая после загрузки всех скриптов.
6 вар _completionCallback;
7 // Контекст (параметры), предоставляемый при выполнении функции обратного вызова.
8 вар _callbackContext;
9
10 // HTTP-элемент (<script />) загружаемого в данный момент скрипта.
11 вар _currentLoadingReference;
12 // Функция обратного вызова, вызываемая после загрузки текущего скрипта.
13 вар _currentOnScriptLoad;
14
15 // Единственный метод ScriptLoader — передать три параметра. Значение параметров не будет повторяться.
16 this.load = функция (ссылки, завершениеCallback, callbackContext) {
17 _references = ссылки;
18 _completionCallback = завершениеCallback;
19 _callbackContext = callbackContext;
20
21 ЗагрузитьСсылки();
двадцать два }
двадцать три
24 // Начинаем загрузку ссылок.
25 функция loadReferences() {
26 // Если в данный момент загружается скрипт.
27 // Это означает, что этот метод не вызывается в первый раз, а загружается в скрипт
28 // Вызывается после завершения для загрузки следующего скрипта.
29, если (_currentLoadingReference) {
30 // Проверяем ReadyState текущего элемента Script, который завершен в IE.
31 // Другие браузеры, такие как FF, загружаются (у FF на самом деле нет этого атрибута.
32 // Но код ниже установит его как загруженный).
33 // Если загрузка не удалась, выходим.
34 if ((_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 // Установите для связанных событий значение null, чтобы гарантировать освобождение ресурсов.
50 if (Sys.Runtime.get_hostType() != Sys.HostType.InternetExplorer) {
51 // Если текущий браузер не IE, см. код ниже
52 // Вы обнаружите, что событие загрузки определено для <script />.
53 _currentLoadingReference.onload = ноль;
54 }
55 еще {
56 // Если это IE, посмотрите код ниже, и вы обнаружите, что
57 // <script /> определяет событие onreadystatechange.
58 _currentLoadingReference.onreadystatechange = ноль;
59 }
60
61 //Наконец освобождаем текущую ссылку <script />.
62 _currentLoadingReference = ноль;
63}
64}
65
66 // Если еще есть незагруженные скрипты.
67, если (_references.length) {
68 // Удаление из очереди.
69 var ссылка = _references.dequeue();
70 // Создать <скрипт />
71 var scriptElement = document.createElement('script');
72 //Устанавливаем текущий <script /> и текущую функцию обратного вызова для успешной загрузки.
73 _currentLoadingReference = scriptElement;
74 _currentOnScriptLoad = reference.onscriptload;
75
76 if (Sys.Runtime.get_hostType() != Sys.HostType.InternetExplorer) {
77 // Если это не IE, то установите атрибут ReadyState для <script />.
78 // И используем событие загрузки.
79 scriptElement.readyState = 'загружено';
80 scriptElement.onload = loadReferences;
81 }
82 еще {
83 // Если это IE, используйте событие onreadystatechange.
84 scriptElement.onreadystatechange = loadReferences;
85}
86 scriptElement.type = 'текст/javascript';
87 scriptElement.src = reference.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 завершениеCallback(callbackContext);
107 }
108
109 _references = ноль;
110 }
111}
112Sys.ScriptLoader.registerClass('Sys.ScriptLoader');
Видно, что метод Sys.ScriptLoader для загрузки сценария заключается в последовательном добавлении элементов <script /> в <header /> через код. На самом деле в Атласе он используется очень редко.
На самом деле код Sys.ScriptLoader очень простой, и добавленные мной комментарии кажутся лишними. Стоит отметить, что все ресурсы высвобождаются максимально. Обратите особое внимание на код, начинающийся со строки 99. Тело if сначала использует временные переменные для сохранения двух глобальных переменных, а затем освобождает глобальные переменные. Его цель — избежать утечек памяти, вызванных исключениями, возникающими при выполнении завершенияCallback, даже если такая возможность существует только одна из десяти тысяч. Чем больше Javascript, тем легче вызвать утечки памяти. Лучше всего обратить внимание на эту проблему при написании JS-кода.
Далее объясните первый параметр метода загрузки — ссылки. Первоначально я думал, что это массив класса Sys.Reference, но обнаружил, что на самом деле это совсем другое. В любом случае, взгляните на код этого класса.
1Sys.Reference = функция() {
2
3 вар _компонент;
4 вар _onload;
5
6 this.get_comComponent = function() {
7 вернуть _компонент;
8}
9 this.set_comComponent = функция (значение) {
10 _компонент = значение;
11 }
12
13 this.get_onscriptload = function() {
14 возврат _onload;
15}
16 this.set_onscriptload = функция(значение) {
17 _onload = значение;
18}
19
20 this.dispose = function() {
21 _компонент = ноль;
двадцать два }
двадцать три
24 this.getDescriptor = function() {
25 var td = новый Sys.TypeDescriptor();
26
27 td.addProperty('компонент', Объект);
28 td.addProperty('onscriptload', String);
29 возвращение тд;
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="server">
6
7</скрипт>
8
9<html xmlns=" http://www.w3.org/1999/xhtml " >
10<head runat="server">
11 <title>Скрипты загрузки</title>
12 <script Language="JavaScript">
13 функция Загрузить()
14 {
15 document.getElementById("bar").style.width = "0px";
16 сценариев var = новый Array();
17 для (var i = 0; i < 8; i++)
18 {
19 вар с = новый объект();
20 var Sleep = Math.round((Math.random() * 400)) + 100;
21 s.url = "Script.ashx?sleep=" + Sleep + "&t=" + Math.random();
22 с.стоимость = сон;
23 скрипта.push(ов);
двадцать четыре }
25
26 Jeffz.Sample.LoadScripts.load(скрипты);
27}
28 </скрипт>
29</head>
30<body style="font-family: Arial;">
31 <form id="form1" runat="server">
32 <дел>
33 <atlas:ScriptManager ID="ScriptManager1" runat="server">
34 <Скрипты>
35 <atlas:ScriptReference Path="js/LoadScripts.js" />
36 </скрипты>
37 </atlas:ScriptManager>
38
39 Индикатор выполнения:
40 <div style="border: сплошной 1 пиксель, черный;">
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 </форма>
47</body>
48</html>
Очень просто. Самый простой индикатор выполнения создается с использованием двух DIV. Функция Load() вызывается при нажатии кнопки. Эта функция случайным образом генерирует ссылки на сценарии и создает массив сценариев из 8 элементов. Формат массива скриптов следующий:
1var скрипты =
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, который используется для имитации длительной загрузки сценария. Он переводит поток в режим ожидания на определенный период времени в зависимости от значения сна в строке запроса (как в следующем t, цель состоит в том, чтобы избежать нажатия кнопки путем изменения кэша строки запроса браузера), в этом файле почти нет кода, и его реализацию можно увидеть в загруженном примере. Наконец, он загружается путем вызова метода Jeffz.Sample.LoadScripts.load, который включает в себя следующий код LoadScripts.js:
1Type.registerNamespace('Jeffz.Sample');
2
3Jeffz.Sample.LoadScripts = новая функция()
4{
5 вар totalCost = 0;
6 вар scriptLoader = новый Sys.ScriptLoader();
7
8 this.load = функция (скрипты)
9 {
10 if (Jeffz.Sample.__onScriptLoad != null)
11 {
12 выдать новую ошибку («В процессе»);
13}
14
15 общая стоимость = 0;
16 Jeffz.Sample.__onScriptLoad = onScriptLoad;
17 ссылок на переменные = новый массив();
18
19 вар loadingCost = 0;
20 для (var i = 0; i <scripts.length; i++)
двадцать один {
22 totalCost += скрипты[i].cost;
23 loadingCost += скрипты[i].cost;
двадцать четыре
25 var ref = createReference(scripts[i].url, LoadedCost);
26
27 ссылок.push(ref);
28 }
29
30 scriptLoader.load(ссылки, onComplete);
31}
32
33 функция createReference(url, loadingCost)
34 {
35 var ref = новый объект();
36 ref.url = URL;
37 ref.onscriptload = "Jeffz.Sample.__onScriptLoad('" + url + "', " + loadingCost + ")";
38 возврат ссылки;
39 }
40
41 функция onComplete()
42 {
43 Jeffz.Sample.__onScriptLoad = ноль;
44}
45
46 функция onScriptLoad (url, LoadedCost)
47 {
48 вар прогресс = 100,0 * LoadedCost / TotalCost;
49 document.getElementById("bar").style.width = прогресс + "%";
50 document.getElementById("message").innerHTML += ("<strong>" + url + "</strong>" + " загружено.<br />");
51 }
52}
Увы, код объяснять, похоже, вообще не нужно. На данный момент завершен простой индикатор выполнения загрузки скрипта, который довольно прост. Код можно скачать, нажав здесь, или просмотреть эффект, нажав здесь.
Но разве на этом дело закончилось? На самом деле меня это решение не очень устраивает, хотя для большинства ситуаций его должно быть достаточно. Вы можете заметить, что я реализовал Jeffz.Sample.LoadScripts как синглтон, то есть другого подобного экземпляра нет. И в начале метода загрузки оценивается, загружается ли он. Если да, то будет выброшено исключение. Прямая причина достижения такой «однопоточной» загрузки заключается в том, что она ограничена реализацией 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