今日、ある人から、163 メールボックスのような Javascript 読み込み進行状況バーを実装する方法を尋ねられました。
わかりませんが、 <script /> には onload と onreadystatechange があるため、実装するのは難しくありません。アトラスもあります。
Atlas には Sys.ScriptLoader というクラスがあり、その機能はページ内の複数のスクリプト ファイルを順番にロードすることです。実装する前に、このクラスのコードを分析しましょう。
1Sys.ScriptLoader = function() {
2
3 //すべてのスクリプトの参照オブジェクトの配列。
4 var _references;
5 //すべてのスクリプトがロードされた後に実行されるコールバック関数。
6 var _completionCallback;
7 // コールバック関数の実行時に提供されるコンテキスト (パラメーター)。
8 var _callbackContext;
9
10 // 現在ロード中のスクリプトの HTTP 要素 (<script />)。
11 var _currentLoadingReference;
12 // 現在のスクリプトがロードされた後に呼び出されるコールバック関数。
13 var _currentOnScriptLoad;
14
15 // ScriptLoader の唯一の方法は 3 つのパラメータを渡すことです。パラメータの意味は繰り返しません。
16 this.load = function(references, completedCallback, callbackContext) {
17 _references = 参照;
18 _completionCallback = 完了コールバック;
19 _callbackContext = コールバックコンテキスト;
20
21 ロードリファレンス();
22 }
23
24 // 参照のロードを開始します。
25 関数loadReferences() {
26 // スクリプトが現在ロード中の場合。
27 // これは、このメソッドが初めて呼び出されるのではなく、スクリプトにロードされることを意味します
28 // 完了後に呼び出され、次のスクリプトをロードします。
29 if (_currentLoadingReference) {
30 // 現在の Script 要素の readState を確認します。これは IE で完了しています。
31 // FF などの他のブラウザがロードされます (FF には実際にはこの属性がありません。
32 // ただし、以下のコードはロード済みに設定します)。
33 // ロードに失敗した場合は終了します。
34 if ((_currentLoadingReference.readyState != 'ロード済み') &&
35 (_currentLoadingReference.readyState != '完了')) {
36 戻ります。
37 }
38 その他 {
39 // このブランチに入ると、ロードが成功したことを示します。
40
41 // 現在のスクリプトが onLoad 関数を定義している場合。
42 if (_currentOnScriptLoad) {
43 // eval 経由で呼び出されます (ここに問題があります)。
44 eval(_currentOnScriptLoad);
45 //リソースを解放するには null に設定します。
46 _currentOnScriptLoad = null;
47 }
48
49 // リソースが確実に解放されるように、関連イベントを null に設定します。
50 if (Sys.Runtime.get_hostType() != Sys.HostType.InternetExplorer) {
51 // 現在のブラウザが IE ではない場合は、以下のコードを参照してください
52 // onload イベントが <script /> に対して定義されていることがわかります。
53 _currentLoadingReference.onload = null;
54 }
55 その他 {
56 // IE の場合は、以下のコードを参照してください。
57 // <script /> は onreadystatechange イベントを定義します。
58 _currentLoadingReference.onreadystatechange = null;
59 }
60
61 //最終的に現在の <script /> 参照を解放します。
62 _currentLoadingReference = null;
63}
64}
65
66 // アンロードされたスクリプトがまだある場合。
67 if (_references.length) {
68 // デキュー。
69 変数参照 = _references.dequeue();
70 // <script /> を作成します
71 var scriptElement = document.createElement('script');
72 //ロードが成功するように現在の <script /> と現在のコールバック関数を設定します。
73 _currentLoadingReference = スクリプト要素;
74 _currentOnScriptLoad = 参照.onscriptload;
75
76 if (Sys.Runtime.get_hostType() != Sys.HostType.InternetExplorer) {
77 // IE でない場合は、<script /> の属性 readState を設定します。
78 // そして onload イベントを使用します。
79 scriptElement.readyState = 'ロード済み';
80 scriptElement.onload = ロード参照;
81 }
82 その他 {
83 // IE の場合は、onreadystatechange イベントを使用します。
84 scriptElement.onreadystatechange =loadReferences;
85}
86 scriptElement.type = 'text/javascript';
87 scriptElement.src = 参照.url;
88
89 // <script /> を DOM に追加します
90 var headElement = document.getElementsByTagName('head')[0];
91 headElement.appendChild(scriptElement);
92
93 戻ります。
94}
95
96 // 実行がこの時点に到達した場合、すべてのスクリプトがロードされたことを意味します。
97 // すべてのスクリプトがロードされた後に実行されるコールバック関数が定義されている場合、
98 // 次に、リソースを実行して解放します。
99 if (_completionCallback) {
100 var completedCallback = _completionCallback;
101 var callbackContext = _callbackContext;
102
103 _completionCallback = null;
104 _callbackContext = null;
105
106 完了コールバック(コールバックコンテキスト);
107 }
108
109 _references = null;
110 }
111}
112Sys.ScriptLoader.registerClass('Sys.ScriptLoader');
Sys.ScriptLoader がスクリプトを読み込む方法は、コードを通じて <script /> 要素を <header /> に順次追加することであることがわかります。実際、Atlas ではほとんど使用されません。
実際、Sys.ScriptLoader のコードは非常に単純で、私が追加したコメントは余分なようです。すべてのリソースが可能な限り解放されることは注目に値します。 99 行目から始まるコードに特に注意してください。 if 本体は、最初に一時変数を使用して 2 つのグローバル変数を保持し、次にグローバル変数を解放します。その目的は、たとえ 10,000 分の 1 の可能性しかないとしても、completionCallback の実行時にスローされる例外によって引き起こされるメモリ リークを回避することです。 Javascript の数が増えると、メモリ リークが発生しやすくなります。JS コードを記述するときは、この問題に注意することをお勧めします。
次に、load メソッドの最初のパラメータであるreferences について説明します。最初はこれが Sys.Reference クラスの配列だと思っていましたが、実際にはまったく異なることがわかりました。とにかく、このクラスのコードを見てください。
1Sys.Reference = function() {
2
3 var _component;
4 var _onload;
5
6 this.get_component = function() {
7 return _component;
8}
9 this.set_component = 関数(値) {
10 _コンポーネント = 値;
11 }
12
13 this.get_onscriptload = function() {
14 _onload を返す;
15}
16 this.set_onscriptload = 関数(値) {
17 _onload = 値;
18}
19
20 this.dispose = function() {
21 _コンポーネント = null;
22 }
23
24 this.getDescriptor = function() {
25 var td = 新しい Sys.TypeDescriptor();
26
27 td.addProperty('コンポーネント', オブジェクト);
28 td.addProperty('onscriptload', String);
29 TD を返します。
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="サーバー">
6
7</script>
8
9<html xmlns=" http://www.w3.org/1999/xhtml " >
10<head runat="サーバー">
11 <title>ロードスクリプト</title>
12 <スクリプト言語="javascript">
13 関数Load()
14 {
15 document.getElementById("bar").style.width = "0px";
16 var script = new Array();
17 for (var i = 0; i < 8; i++)
18 {
19 var s = 新しいオブジェクト();
20 var sleep = Math.round((Math.random() * 400)) + 100;
21 s.url = "Script.ashx?sleep=" + sleep + "&t=" + Math.random();
22 秒コスト = 睡眠;
23 個の script.push;
24 }
25
26 Jeffz.Sample.LoadScripts.load(スクリプト);
27}
28 </スクリプト>
29</頭>
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:スクリプトマネージャー>
38
39 プログレスバー:
40 <div style="border: ソリッド 1px 黒;">
41 <div id="bar" style="高さ: 20px; 幅: 0%; 背景色: Red;"></div>
42 </div>
43 <input type="button" onclick="Load()" value="Load" />
44 <div id="メッセージ"></div>
45 </div>
46 </form>
47</ボディ>
48</html>
とてもシンプルです。最も単純なプログレス バーは 2 つの DIV を使用して作成されます。ボタンがクリックされると、Load() 関数が呼び出されます。この関数は、スクリプト リンクをランダムに生成し、8 要素のスクリプト配列を生成します。スクリプト配列の形式は次のとおりです:
1var scripts =
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 があることがわかります。これは、クエリ文字列の sleep の値に基づいて一定期間スレッドをスリープさせます (次の t のように)。目的は、クエリ文字列のブラウザ キャッシュを変更してボタンのクリックを回避することだけです)。このファイルにはコードがほとんどなく、その実装はサンプル ダウンロードで確認できます。最後に、Jeffz.Sample.LoadScripts.load メソッドを呼び出してロードします。これには、次のコード LoadScripts.js が含まれます
。
2
3Jeffz.Sample.LoadScripts = 新しい関数()
4{
5 var totalCost = 0;
6 var scriptLoader = 新しい Sys.ScriptLoader();
7
8 this.load = 関数(スクリプト)
9 {
10 if (Jeffz.Sample.__onScriptLoad != null)
11 {
12 throw new Error("進行中");
13}
14
15 合計コスト = 0;
16 Jeffz.Sample.__onScriptLoad = onScriptLoad;
17 var 参照 = new Array();
18
19 varloadedCost = 0;
20 for (var i = 0; i < scripts.length; i++)
21 {
22 totalCost += scripts[i].cost;
23 ロードコスト += スクリプト[i].コスト;
24
25 var ref = createReference(scripts[i].url,loadedCost);
26
27 参照.push(ref);
28 }
29
30 scriptLoader.load(references, onComplete);
31}
32
33 関数createReference(url、loadedCost)
34 {
35 var ref = 新しいオブジェクト();
36 参照 URL = URL;
37 ref.onscriptload = "Jeffz.Sample.__onScriptLoad('" + url + "', " +loadedCost + ")";
38 戻り値;
39 }
40
41 関数 onComplete()
42 {
43 Jeffz.Sample.__onScriptLoad = null;
44}
45
46 関数onScriptLoad(url,loadedCost)
47 {
48 var progress = 100.0 *loadedCost / totalCost;
49 document.getElementById("bar").style.width = 進行状況 + "%";
50 document.getElementById("メッセージ").innerHTML += ("<strong>" + URL + "</strong>" + " 読み込まれました。<br />");
51 }
52}
残念ながら、コードを説明する必要はまったくないようです。ここまでで、非常にシンプルなスクリプト読み込み進行状況バーが完成しました。ここをクリックしてコードをダウンロードするか、ここをクリックして効果を確認できます。
しかし、それで問題は終わったのでしょうか?実際、私はこの解決策にあまり満足していませんが、ほとんどの状況では十分であるはずです。 Jeffz.Sample.LoadScripts をシングルトンとして実装したことがわかります。つまり、これに似たインスタンスは他にありません。そして、load メソッドの開始時にロード中かどうかが判断され、ロード中であれば例外がスローされます。このような "シングル スレッド" 読み込みを実現する直接の理由は、Sys.ScriptLoader の実装によって制限されるためです。
Sys.ScriptLoader コードの 44 行目を見てください。スクリプトのロード時に eval を使用して「悪意のある」コールバックが行われています。 eval ではコールバック関数として関数への参照を渡すことができないため、これは実際には開発者にとって非常に不快な実装です。できることは、「ルート コード」を文字列として Sys.ScriptLoader に渡すことだけです。 Sys.ScriptLoader を使用してスクリプトの「同時」読み込みを実現することはまだ可能ですが (率直に言えば、せいぜい Sys.ScriptLoader のようなキューを作成することができます)、コード量は当然増加し、開発の複雑さも増加します。増加。
ただし、ほとんどの状況では、この「シングルスレッド」スクリプトの読み込みで十分だと思います。そして、本当に「特別な」要件がある場合、大多数の開発者にとって、このような Sys.ScriptLoader の明確な例を参照して要件を書き直すのは簡単ではないでしょうか?
http://www.cnblogs.com/JeffreyZhao/archive/2006/09/13/502357.html