国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

Sys.ScriptLoader與JS加載進(jìn)度條的實(shí)現(xiàn)

2019-11-18 17:00:21
字體:
供稿:網(wǎng)友

  今天有人問我,163郵箱那樣的javascript加載進(jìn)度條是如何實(shí)現(xiàn)的。

  我不知道,不過實(shí)現(xiàn)一個(gè)不難,因?yàn)?lt;script />有onload和onreadystatechange。還有就是,我們有Atlas。

  Atlas中有個(gè)類:Sys.ScriptLoader,它的作用就是在頁面中依次地加載多個(gè)Script文件。在實(shí)現(xiàn)之前,先來分析一下這個(gè)類的代碼。

 

  1Sys.ScriptLoader = function() {
  2
  3    // 所有Script的reference對象數(shù)組。
  4    var _references;
  5    // 所有Script加載完之后執(zhí)行的回調(diào)函數(shù)。
  6    var _completionCallback;
  7    // 執(zhí)行回調(diào)函數(shù)時(shí)提供的上下文(參數(shù))。
  8    var _callbackContext;
  9
 10    // 當(dāng)前正在加載的Script的HTTP Element(<script />)。
 11    var _currentLoadingReference;
 12    // 當(dāng)前的Script加載完成后所調(diào)用的回調(diào)函數(shù)。
 13    var _currentOnScriptLoad;
 14   
 15    // ScriptLoader唯一的方法,傳入三個(gè)參數(shù),參數(shù)含義不再贅述。
 16    this.load = function(references, completionCallback, callbackContext) {
 17        _references = references;
 18        _completionCallback = completionCallback;
 19        _callbackContext = callbackContext;
 20       
 21        loadReferences();
 22    }
 23
 24    // 開始加載引用。
 25    function loadReferences() {
 26        // 如果當(dāng)前正在加載某個(gè)Script。
 27        // 這表示此方法不是第一次被調(diào)用,而是在某個(gè)Script被加載
 28        // 完成后才被調(diào)用,用以加載下一個(gè)Script。
 29        if (_currentLoadingReference) {
 30            // 查看當(dāng)前Script元素的readyState,IE下為complete,
 31            // 其他瀏覽器如FF則為loaded(FF其實(shí)并無此屬性,
 32            // 但是下面的代碼會將其設(shè)為loaded)。
 33            // 如果加載失敗,則退出。
 34            if ((_currentLoadingReference.readyState != 'loaded') &&
 35                (_currentLoadingReference.readyState != 'complete')) {
 36                return;
 37            }
 38            else {
 39                // 進(jìn)入此分支,表明加載成功。
 40               
 41                // 如果當(dāng)前Script定義了onLoad函數(shù)。
 42                if (_currentOnScriptLoad) {
 43                    // 通過eval調(diào)用(這里是個(gè)麻煩的地方)。
 44                    eval(_currentOnScriptLoad);
 45                    // 設(shè)為null,釋放資源。
 46                    _currentOnScriptLoad = null;
 47                }
 48               
 49                // 將相關(guān)事件設(shè)為null以確保釋放資源。
 50                if (Sys.Runtime.get_hostType() != Sys.HostType.InternetExplorer) {
 51                    // 如果當(dāng)前瀏覽器不是IE,見下面的代碼
 52                    // 會發(fā)現(xiàn)為<script />定義了onload事件。
 53                    _currentLoadingReference.onload = null;
 54                }
 55                else {
 56                    // 如果是IE,見下面代碼會發(fā)現(xiàn)為了
 57                    // <script />定義了onreadystatechange事件。
 58                    _currentLoadingReference.onreadystatechange = null;
 59                }
 60               
 61                // 最終釋放當(dāng)前的<script />引用。
 62                _currentLoadingReference = null;
 63            }
 64        }
 65
 66        // 如果還有沒有加載的Script。
 67        if (_references.length) {
 68            // 出隊(duì)列。
 69            var reference = _references.dequeue();
 70            // 創(chuàng)建<script />
 71            var scriptElement = document.createElement('script');
 72            // 設(shè)當(dāng)前的<script />和當(dāng)前加載成功的回調(diào)函數(shù)。
 73            _currentLoadingReference = scriptElement;
 74            _currentOnScriptLoad = reference.onscriptload;
 75           
 76            if (Sys.Runtime.get_hostType() != Sys.HostType.InternetExplorer) {
 77                // 如果不是IE的話,那么為<script />設(shè)屬性readyState,
 78                // 并且使用onload事件。
 79                scriptElement.readyState = 'loaded';
 80                scriptElement.onload = loadReferences;
 81            }
 82            else {
 83                // 如果是IE,那么使用onreadystatechange事件。
 84                scriptElement.onreadystatechange = loadReferences;
 85            }
 86            scriptElement.type = 'text/Javascript';
 87            scriptElement.src = reference.url;
 88
 89            // 將<script />添加至DOM
 90            var headElement = document.getElementsByTagName('head')[0];
 91            headElement.appendChild(scriptElement);
 92
 93            return;
 94        }
 95       
 96        // 如果執(zhí)行到這里,說明所有的Script已經(jīng)加載完了。
 97        // 如果定義了所有Script加載完之后執(zhí)行的回調(diào)函數(shù),
 98        // 那么執(zhí)行并釋放資源。
 99        if (_completionCallback) {
100            var completionCallback = _completionCallback;
101            var callbackContext = _callbackContext;
102           
103            _completionCallback = null;
104            _callbackContext = null;
105           
106            completionCallback(callbackContext);
107        }
108       
109        _references = null;
110    }
111}
112Sys.ScriptLoader.registerClass('Sys.ScriptLoader');
  可以看出,Sys.ScriptLoader加載script的方法就是通過代碼依次向<header />里添加<script />元素。事實(shí)上,它在Atlas中被使用的非常少。

  事實(shí)上,Sys.ScriptLoader的代碼非常簡單,我添加的注釋越看越像畫蛇添足。值得注意的是所有的資源都被盡可能的釋放。尤其注意從第99行開始的代碼,if體內(nèi)首先用臨時(shí)變量保留兩個(gè)全局變量,然后再將全局變量釋放。其目的就是避免在completionCallback在執(zhí)行時(shí)拋出異常而導(dǎo)致的內(nèi)存泄露,即使只有萬分之一的可能性。Javascript越多,則越容易造成內(nèi)存泄露,在編寫JS代碼時(shí)最好注意這方面的問題。

  接著解釋一下load方法的第一個(gè)參數(shù)references,原本以為這一個(gè)Sys.Reference類的數(shù)組,結(jié)果發(fā)現(xiàn)其實(shí)相差甚遠(yuǎn)。不管怎么樣順便看一下該類的代碼。

 

 1Sys.Reference = function() {
 2
 3    var _component;
 4    var _onload;
 5   
 6    this.get_component = function() {
 7        return _component;
 8    }
 9    this.set_component = function(value) {
10        _component = value;
11    }
12   
13    this.get_onscriptload = function() {
14        return _onload;
15    }
16    this.set_onscriptload = function(value) {
17        _onload = value;
18    }
19   
20    this.dispose = function() {
21        _component = null;
22    }
23   
24    this.getDescriptor = function() {
25        var td = new Sys.TypeDescriptor();
26       
27        td.add28        td.addProperty('onscriptload', String);
29        return td;
30    }
31}
32Sys.Reference.registerSealedClass('Sys.Reference', null, Sys.ITypeDescriptorProvider, Sys.IDisposable);
33Sys.TypeDescriptor.addType('script', 'reference', Sys.Reference);
  關(guān)心一下Sys.ScriptLoader類的代碼可知,reference數(shù)組的每個(gè)元素其實(shí)只是簡單的“{ url : "

  到這里,我想大家也應(yīng)該想到了如何使用Sys.ScriptLoader輕而易舉地制作JS加載的進(jìn)度條。不過既然寫到了這里,也就繼續(xù)把它進(jìn)行一個(gè)簡單的實(shí)現(xiàn)。

  首先是aspx文件。

 1<%@ Page Language="C#" %>
 2
 3<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " 4
 5<script runat="server">
 6
 7</script>
 8
 9<html
xmlns="10<head runat="server">
11    <title>Load Scripts</title>
12    <script language="javascript">
13        function Load()
14        {
15            document.getElementById("bar").style.width = "0px";
16            var scripts = new Array();
17            for (var i = 0; i < 8; i++)
18            {
19                var s = new Object();
20                var sleep = Math.round((Math.random() * 400)) + 100;
21                s.url = "Script.ashx?sleep=" + sleep + "&t=" + Math.random();
22                s.cost = sleep;
23                scripts.push(s);
24            }
25               
26            Jeffz.Sample.LoadScripts.load(scripts);
27        }
28    </script>
29</head>
30<body style="font-family: Arial;">
31    <form id="form1" runat="server">
32    <div>
33        <atlas:ScriptManager ID="ScriptManager1" runat="server">
34            <Scripts>
35                <atlas:ScriptReference Path="js/LoadScripts.js" />
36            </Scripts>
37        </atlas:ScriptManager>
38
39        Progress Bar:       
40        <div style="border: solid 1px black;">
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    </form>
47</body>
48</html>
  非常的簡單。使用兩個(gè)DIV制作了一個(gè)最簡單的進(jìn)度條。在點(diǎn)擊按鈕時(shí)調(diào)用了Load()函數(shù)。該函數(shù)隨機(jī)生成了Script鏈接并生成了一個(gè)8元素的scripts數(shù)組。scripts數(shù)組的格式如下:

1var scripts =
2[
3    { url : "
4    { url : "5    { url : "6];
  每個(gè)元素的url屬性不必說,而cost的功能就是表示加載該文件所消耗的時(shí)間的值。這個(gè)值沒有單位,用到的只是這個(gè)值在總共消耗里的比例。另外,可以看到有一個(gè)Script.ashx,其作用是模擬一個(gè)長時(shí)間script加載,它會根據(jù)querystring中的sleep的值將線程休眠一段時(shí)間(至于后面的t,目的只是通過改變querystring來避免點(diǎn)擊按鈕時(shí)瀏覽器的緩存),這個(gè)文件幾乎沒有代碼,可以在范例下載中看到它的實(shí)現(xiàn)。最后通過調(diào)用Jeffz.Sample.LoadScripts.load方法進(jìn)行加載,這就涉及到了下面的代碼,LoadScripts.js:

 1Type.registerNamespace('Jeffz.Sample');
 2
 3Jeffz.Sample.LoadScripts = new function()
 4{
 5    var totalCost = 0;
 6    var scriptLoader = new Sys.ScriptLoader();
 7
 8    this.load = function(scripts)
 9    {
10        if (Jeffz.Sample.__onScriptLoad != null)
11        {
12            throw new Error("In progress");
13        }
14       
15        totalCost = 0;
16        Jeffz.Sample.__onScriptLoad = onScriptLoad;
17        var references = new Array();
18   
19        var loadedCost = 0;
20        for (var i = 0; i < scripts.length; i++)
21        {
22            totalCost += scripts[i].cost;
23            loadedCost += scripts[i].cost;
24           
25            var ref = createReference(scripts[i].url, loadedCost);
26           
27            references.push(ref);
28        }
29       
30        scriptLoader.load(references, onComplete);
31    }
32   
33    function createReference(url, loadedCost)
34    {
35        var ref = new Object();
36        ref.url = url;
37        ref.onscriptload = "Jeffz.Sample.__onScriptLoad('" + url + "', " + loadedCost + ")";
38        return ref;
39    }
40   
41    function onComplete()
42    {
43        Jeffz.Sample.__onScriptLoad = null;
44    }
45   
46    function onScriptLoad(url, loadedCost)
47    {
48        var progress = 100.0 * loadedCost / totalCost;
49        document.getElementById("bar").style.width = progress + "%";
50        document.getElementById("message").innerHTML += ("<strong>" + url + "</strong>" + " loaded.<br />");
51    }
52}
  哎,似乎完全沒有必要對代碼進(jìn)行多余的解釋。到目前為止,一個(gè)簡單的Script加載進(jìn)度條就完成了,相當(dāng)?shù)暮唵巍4a可以點(diǎn)擊這里下載,也可以點(diǎn)擊這里查看效果。

  不過事情到此為止了嗎?事實(shí)上,我對這個(gè)Solution不怎么滿意,雖然對于大多數(shù)情況應(yīng)該已經(jīng)夠用了。可以注意到,我將Jeffz.Sample.LoadScripts實(shí)現(xiàn)成為了一個(gè)Singleton,也就是說,沒有另外一個(gè)和它一樣的實(shí)例。并且在load方法的一開始就判斷是不是正在加載,如果是,那么會拋出一個(gè)異常。實(shí)現(xiàn)了這么一種“單線程”的加載,直接原因是受限于Sys.ScriptLoader的實(shí)現(xiàn)。

  請看Sys.ScriptLoader代碼的第44行,它使用了eval來“邪惡”地進(jìn)行了script加載完成時(shí)的回調(diào)。這其實(shí)對于開發(fā)人員是一種非常難受的實(shí)現(xiàn),因?yàn)閑val,所以無法地將一個(gè)函數(shù)的引用作為回調(diào)函數(shù)來傳遞。唯一能做的就是只能把“根代碼”作為字符串形式來交給Sys.ScriptLoader。雖然還是能夠通過Sys.ScriptLoader實(shí)現(xiàn)“并發(fā)”的Script加載(說白了最多像Sys.ScriptLoader一樣建一個(gè)隊(duì)列嘛),但是代碼量自然而然就上去了,開發(fā)的復(fù)雜度也提高了。

  不過我認(rèn)為,這種“單線程”的script加載已經(jīng)足夠用于大多數(shù)情況了。而且如果真的有“特殊”要求,參照Sys.ScriptLoader這個(gè)如此清晰明了的范例,自己重新寫一個(gè)對于廣大開發(fā)人員來說,難道還不是易如反掌的事情嗎?
http://m.survivalescaperooms.com/JeffreyZhao/archive/2006/09/13/502357.html


發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 松潘县| 广宗县| 封丘县| 万山特区| 龙海市| 甘洛县| 余江县| 上思县| 施甸县| 兴和县| 独山县| 临猗县| 宿迁市| 太白县| 阿鲁科尔沁旗| 手游| 张掖市| 平江县| 永川市| 宝兴县| 新野县| 大丰市| 西贡区| 福贡县| 北安市| 古蔺县| 濮阳市| 怀集县| 邢台市| 云梦县| 靖江市| 河西区| 应用必备| 玉溪市| 美姑县| 临漳县| 锡林浩特市| 厦门市| 潮州市| 仁化县| 兰州市|