오늘 누군가가 163 메일함과 같은 Javascript 로딩 진행 표시줄을 구현하는 방법을 물었습니다.
모르겠지만 <script />에는 onload와 onreadystatechange가 있으므로 구현하는 것은 어렵지 않습니다. 또한 Atlas도 있습니다.
Atlas에는 Sys.ScriptLoader라는 클래스가 있습니다. 이 클래스의 기능은 페이지에 여러 스크립트 파일을 순서대로 로드하는 것입니다. 구현하기 전에 이 클래스의 코드를 분석해 보겠습니다.
1Sys.ScriptLoader = 함수() {
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의 유일한 방법은 세 개의 매개변수를 전달하는 것입니다. 매개변수의 의미는 반복되지 않습니다.
16 this.load = function(references, achievementCallback, callbackContext) {
17 _참조 = 참조;
18 _completionCallback = 완성콜백;
19 _callbackContext = 콜백컨텍스트;
20
21 로드참조();
스물 둘 }
스물셋
24 // 참조 로드를 시작합니다.
25 함수 loadReferences() {
26 // 스크립트가 현재 로드 중인 경우.
27 // 이는 이 메소드가 처음으로 호출되지 않고 스크립트에 로드된다는 의미입니다.
28 // 완료 후 다음 스크립트를 로드하기 위해 호출됩니다.
29 if (_currentLoadingReference) {
30 // IE에서 완료된 현재 Script 요소의 ReadyState를 확인합니다.
31 // FF 등 다른 브라우저가 로드됩니다(FF에는 실제로 이 속성이 없습니다.
32 // 그러나 아래 코드는 로드되도록 설정합니다.)
33 // 로딩에 실패하면 종료합니다.
34 if ((_currentLoadingReference.readyState != 'loaded') &&
35 (_currentLoadingReference.readyState != '완료')) {
36 반환;
37 }
38 그밖에 {
39 // 이 분기에 들어가면 성공적인 로딩을 의미합니다.
40
41 // 현재 스크립트가 onLoad 함수를 정의하는 경우.
42 if (_currentOnScriptLoad) {
43 // eval을 통해 호출됩니다(여기에 문제가 있습니다).
44 평가(_currentOnScriptLoad);
45 //리소스를 해제하려면 null로 설정하세요.
46 _currentOnScriptLoad = null;
47 }
48
49 // 리소스가 해제되도록 관련 이벤트를 null로 설정합니다.
50 if (Sys.Runtime.get_hostType() != Sys.HostType.InternetExplorer) {
51 // 현재 브라우저가 IE가 아닌 경우 아래 코드를 참고하세요
52 // <script />에 대해 onload 이벤트가 정의되어 있음을 알 수 있습니다.
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 var 참조 = _references.dequeue();
70 // <스크립트 /> 생성
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 />에 대해 ReadyState 속성을 설정합니다.
78 // 그리고 onload 이벤트를 사용합니다.
79 scriptElement.readyState = '로드됨';
80 scriptElement.onload = 로드References;
81 }
82 그 밖의 {
83 // IE라면 onreadystatechange 이벤트를 사용하세요.
84 scriptElement.onreadystatechange = 로드References;
85}
86 scriptElement.type = '텍스트/자바스크립트';
87 scriptElement.src = 참조.url;
88
89 // DOM에 <script /> 추가
90 var headElement = document.getElementsByTagName('head')[0];
91 headElement.appendChild(scriptElement);
92
93 반환;
94}
95
96 // 실행이 이 지점에 도달하면 모든 스크립트가 로드되었음을 의미합니다.
97 // 모든 스크립트가 로드된 후 실행되는 콜백 함수를 정의하면,
98 // 그런 다음 리소스를 실행하고 해제합니다.
99 if (_completionCallback) {
100 var 완성Callback = _completionCallback;
101 var callbackContext = _callbackContext;
102
103 _completionCallback = null;
104 _callbackContext = null;
105
106 완성콜백(callbackContext);
107 }
108
109_참조 = null;
110 }
111}
112Sys.ScriptLoader.registerClass('Sys.ScriptLoader');
Sys.ScriptLoader가 스크립트를 로드하는 방법은 코드를 통해 <header />에 <script /> 요소를 순차적으로 추가하는 것임을 알 수 있다. 실제로 Atlas에서는 거의 사용되지 않습니다.
사실 Sys.ScriptLoader의 코드는 매우 간단하고, 제가 추가한 주석도 불필요한 것 같습니다. 모든 리소스가 가능한 한 많이 공개된다는 점은 주목할 가치가 있습니다. 99행부터 시작하는 코드에 특히 주의하세요. if 본문은 먼저 임시 변수를 사용하여 두 개의 전역 변수를 유지한 다음 전역 변수를 해제합니다. 그 목적은 CompletionCallback이 실행될 때 발생하는 예외로 인해 발생하는 메모리 누수를 방지하는 것입니다. 가능성이 1만 분의 1에 불과하더라도 마찬가지입니다. Javascript가 많을수록 메모리 누수가 발생하기 쉽습니다. JS 코드를 작성할 때 이 문제에 주의하는 것이 가장 좋습니다.
다음으로 로드 메서드의 첫 번째 매개 변수인 참조에 대해 설명합니다. 처음에는 이것이 Sys.Reference 클래스의 배열인 줄 알았는데 실제로는 꽤 달랐습니다. 어쨌든 이 클래스의 코드를 살펴보세요.
1Sys.Reference = 함수() {
2
3변수 _컴포넌트;
4 var _onload;
5
6 this.get_comComponent = 함수() {
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 = 함수() {
21 _컴포넌트 = null;
스물 둘 }
스물셋
24 this.getDescriptor = 함수() {
25 var td = new Sys.TypeDescriptor();
26
27 td.addProperty('컴포넌트', Object);
28 td.addProperty('onscriptload', String);
29 반환 td;
30}
31}
32Sys.Reference.registerSealedClass('Sys.Reference', null, Sys.ITypeDescriptorProvider, Sys.IDisposable);
33Sys.TypeDescriptor.addType('script', 'reference', Sys.Reference);
Sys.ScriptLoader 클래스의 코드를 살펴보면 참조 배열의 각 요소가 실제로는 단순한 "{ url : " http://www.sample.com/sample.js ", onscriptload : "임을 알 수 있습니다. 경고(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 <스크립트 언어="자바스크립트">
13 함수 로드()
14 {
15 document.getElementById("bar").style.width = "0px";
16 var scripts = new Array();
17(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개의 스크립트.푸시(들);
스물넷 }
25
26 Jeffz.Sample.LoadScripts.load(스크립트);
27}
28 </script>
29</head>
30<body style="font-family: Arial;">
31 <form id="form1" runat="서버">
32 <div>
33 <atlas:ScriptManager ID="ScriptManager1" runat="서버">
34 <스크립트>
35 <atlas:ScriptReference Path="js/LoadScripts.js" />
36 </스크립트>
37 </atlas:스크립트매니저>
38
39 진행 표시줄:
40 <div style="테두리: 단색 1px 검정색;">
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="메시지"></div>
45 </div>
46 </form>
47</body>
48</html>
매우 간단합니다. 가장 간단한 진행 표시줄은 두 개의 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의 경우). 목적은 쿼리 문자열 브라우저 캐시를 변경하여 버튼 클릭을 방지하는 것입니다. 이 파일에는 코드가 거의 없으며 해당 구현은 샘플 다운로드에서 볼 수 있습니다. 마지막으로 LoadScripts.js 코드가 포함된 Jeffz.Sample.LoadScripts.load 메서드를 호출하여 로드됩니다.
1Type.registerNamespace('Jeffz.Sample');
2
3Jeffz.Sample.LoadScripts = 새 함수()
4{
5 var totalCost = 0;
6 var scriptLoader = new 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 var loadCost = 0;
20(var i = 0; i < scripts.length; i++)
스물 하나 {
22 totalCost += 스크립트[i].cost;
23loadCost += 스크립트[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,loadedCost)
34 {
35 var ref = new Object();
36 ref.url = URL;
37 ref.onscriptload = "Jeffz.Sample.__onScriptLoad('" + url + "', " +loadCost + ")";
38 반환 참조;
39 }
40
41 함수 onComplete()
42 {
43 Jeffz.Sample.__onScriptLoad = null;
44}
45
46 함수 onScriptLoad(url,loadedCost)
47 {
48 var 진행률 = 100.0 * loadingCost / totalCost;
49 document.getElementById("bar").style.width = 진행률 + "%";
50 document.getElementById("message").innerHTML += ("<strong>" + url + "</strong>" + " 로드됨.<br />");
51 }
52}
아아, 코드를 전혀 설명할 필요가 없는 것 같습니다. 지금까지 간단한 스크립트 로딩 진행 표시줄이 완성되었는데, 이는 매우 간단합니다. 여기를 클릭하면 코드를 다운로드할 수 있고, 여기를 클릭하면 효과를 볼 수 있습니다.
하지만 그게 문제의 끝인가요? 사실 저는 이 솔루션이 대부분의 상황에 충분하지만 그다지 만족스럽지 않습니다. Jeffz.Sample.LoadScripts를 싱글톤으로 구현했다는 것을 알 수 있습니다. 즉, 이와 같은 다른 인스턴스는 없습니다. 그리고 로드 메소드의 시작 부분에서 로드 중인지 판단합니다. 그렇다면 예외가 발생합니다. 이러한 "단일 스레드" 로딩을 달성하는 직접적인 이유는 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