js文件 與 css文件 異步加載

 

使用lazyload 異步加載css js 文件. 提高頁面初始化的速度,減小卡頓時間 ,
下面是 使用方法 與 lazyload.js 源碼 (中文註釋) 
調用方法後. 會追加到 head 標籤末尾
會按照 數組中的地址順序 進行加載文件
加載成功後有 console.log().進行日誌打印
不須要的話,能夠刪除.

有興趣的能夠看看註釋, 研究一下LazyLoad.js 的 實現方式.

完整例子

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>test</title>
    <meta name="Keywords" content="">
    <meta name="Description" content="">
    <!--先 引入lazyload 再調用方法--> 
    <script src="/js/JsTool/CssJsFileLazyLoad/lazyload.js"></script>
    <script async="async"> var cssFile = [ '/css/backstage/1.css', '/css/backstage/2.css', '/css/backstage/3.css', '/css/backstage/4.css', '/css/backstage/5.css', '/css/backstage/6.css' ]; //調用方法後. 會追加到 head 標籤末尾 //會按照 數組中的順序 進行加載文件 LazyLoad.css(cssFile, function () { console.log("css加載完成..."); }); </script>
</head>
<body class="bg_0"> 
	<div>內容</div>
	
    <script async="async"> var jsFile = [ '/js/backstage/jquery/1.js', '/js/backstage/jquery/2.js', '/js/backstage/jquery/3.js', '/js/backstage/jquery/4.js', '/js/backstage/jquery/5.js', '/js/backstage/jquery/6.js' ]; //調用方法後. 會追加到 head 標籤末尾 LazyLoad.js(jsFile, function () { console.log("js加載完成..."); }); </script>
</body>
</html>

新建文件 LazyLoad.js 內容以下

/** * 異步加載css 與 js 文件 * xue * */
LazyLoad = (function (doc) {
    /** * 如何使用: * * CSS: * var cssFile = [ '/css/backstage/1.css', '/css/backstage/2.css', '/css/backstage/3.css', '/css/backstage/4.css', '/css/backstage/5.css', '/css/backstage/6.css' ]; //能夠使用數組的形式,加載多個css文件. 也能夠傳入一個字符串,加載一個css LazyLoad.css(cssFile, function () { console.log("css加載完成..."); }); * *JS: var jsFile = [ '/js/backstage/jquery/1.js', '/js/backstage/jquery/2.js', '/js/backstage/jquery/3.js', '/js/backstage/jquery/4.js', '/js/backstage/jquery/5.js', '/js/backstage/jquery/6.js' ]; //能夠使用數組的形式,加載多個js文件. 也能夠傳入一個字符串,加載一個js LazyLoad.js(jsFile, function () { console.log("js加載完成..."); }); */

    // Private Property --------------------------------------------------------

    var env,
        head,
        pending = {},
        pollCount = 0,
        queue = { css: [], js: [] },
        styleSheets = doc.styleSheets,
        startTime,
        endTime;

    // Private Methods --------------------------------------------------------

    /** 建立並返回具備指定名稱和屬性的HTML元素。 @method createNode @param {String} name 元素名 @param {Object} attrs 元素屬性的 名稱/值 映射 @return {HTMLElement} @private */
    function createNode(name, attrs) {
        var node = doc.createElement(name), attr;

        for (attr in attrs) {
            if (attrs.hasOwnProperty(attr)) {
                node.setAttribute(attr, attrs[attr]);
            }
        }

        return node;
    }

    /** 當指定類型的當前掛起資源完成時調用裝載。執行關聯的回調(若是有)並加載下一個回調隊列中的資源。 @method finish @param {String} type 資源類型 ('css' or 'js') @private */
    function finish(type) {
        var p = pending[type], callback, urls;

        if (p) {
            callback = p.callback;
            urls = p.urls;

            urls.shift();
            pollCount = 0;

            // 若是這是最後一個掛起的url,則執行回調和
            // 啓動隊列中的下一個請求(若是有)。
            if (!urls.length) {
                callback && callback.call(p.context, p.obj);
                pending[type] = null;
                queue[type].length && load(type);
            }
        }
    }

    /** 填充 <code>env</code> 變量帶有用戶代理和特性測試信息。 @method getEnv @private */
    function getEnv() {
        var ua = navigator.userAgent;

        env = {
            //若是此瀏覽器支持動態禁用異步模式,則爲True
            //建立腳本節點
            async: doc.createElement('script').async === true
        };

        (env.webkit = /AppleWebKit\//.test(ua))
            || (env.ie = /MSIE|Trident/.test(ua))
            || (env.opera = /Opera/.test(ua))
            || (env.gecko = /Gecko\//.test(ua))
            || (env.unknown = true);
    }

    /** 加載指定的資源或指定類型的下一個資源 若是沒有指定資源,則在隊列中。若是指定的資源 類型已加載,新請求將排隊,直到 第一個請求已經完成。 當指定資源url數組時,將加載這些url 若是可能的話,在保持執行順序的同時並行執行。全部 瀏覽器支持CSS的並行加載,但只支持Firefox和Opera 支持腳本的並行加載。在其餘瀏覽器中,腳本將是 排隊並一次加載一個,以確保正確的執行順序。 @method load @param {String} type 資源類型 ('css' or 'js') @param {String|Array} urls (optional) 要加載的URL或URL數組 @param {Function} callback (optional) 回調函數 @param {Object} obj (optional) 對象傳遞給回調函數 @param {Object} context (optional) 若是提供,則回調函數將在這個對象的上下文中執行 @private */
    function load(type, urls, callback, obj, context) {
   		//開始計時
        startTime = new Date().getTime();
        
        var _finish = function () { finish(type); },
            isCSS = type === 'css',
            nodes = [],
            i, len, node, p, pendingUrls, url;

        env || getEnv();

        if (urls) {
            //若是url是字符串,則將其包裝在數組中。不然假設它是
            //數組並建立它的副本,這樣就不會對其進行修改
            urls = typeof urls === 'string' ? [urls] : urls.concat();

            // 爲每一個URL建立一個請求對象。若是指定了多個url,
            // 回調只會在加載全部url以後執行。
            //
            //遺憾的是,Firefox和Opera是惟一可以加載的瀏覽器
            //腳本並行,同時保持執行順序。在全部其餘
            //瀏覽器,腳本必須順序加載。
            //
            //全部瀏覽器都根據連接的順序尊重CSS的特性
            // DOM中的元素,而不考慮樣式表的順序
            //其實是下載的。
            if (isCSS || env.async || env.gecko || env.opera) {
                // 並行加載
                queue[type].push({
                    urls: urls,
                    callback: callback,
                    obj: obj,
                    context: context
                });
            } else {
                // 加載順序。
                for (i = 0, len = urls.length; i < len; ++i) {
                    queue[type].push({
                        urls: [urls[i]],
                        callback: i === len - 1 ? callback : null, // 回調只添加到最後一個URL
                        obj: obj,
                        context: context
                    });
                }
            }
        }

        //若是以前的這種類型的加載請求正在進行中,那麼咱們將
        //輪到咱們了。不然,獲取隊列中的下一項。
        if (pending[type] || !(p = pending[type] = queue[type].shift())) {
            return;
        }
		//獲取head標籤
        head || (head = doc.head || doc.getElementsByTagName('head')[0]);
        pendingUrls = p.urls.concat();

        for (i = 0, len = pendingUrls.length; i < len; ++i) {
            url = pendingUrls[i];
            //開始拼接 標籤
            if (isCSS) {
                node = env.gecko ? createNode('style') : createNode('link', {
                    href: url,
                    rel: 'stylesheet'
                });
            } else {
                node = createNode('script', { src: url });
                node.async = false;
            }

            node.className = 'lazyload';
            node.setAttribute('charset', 'utf-8');

            if (env.ie && !isCSS && 'onreadystatechange' in node && !('draggable' in node)) {
                node.onreadystatechange = function () {
                    if (/loaded|complete/.test(node.readyState)) {
                        node.onreadystatechange = null;
                        _finish();
                    }
                };
            } else if (isCSS && (env.gecko || env.webkit)) {
                // Gecko和WebKit不支持連接節點上的onload事件。
                if (env.webkit) {
                    //在WebKit中,咱們能夠輪詢對文檔的更改。樣式表
                    //肯定樣式表什麼時候加載。
                    p.urls[i] = node.href; //解析相對url(或輪詢不起做用)
                    pollWebKit();
                } else {
                    //在Gecko中,咱們能夠將請求的URL導入到<style>節點中
                    //輪詢node.sheet.cssRules是否存在。
                    node.innerHTML = '@import "' + url + '";';
                    pollGecko(node);
                }
            } else {
                node.onload = node.onerror = _finish;
            }

            nodes.push(node);
        }

        for (i = 0, len = nodes.length; i < len; ++i) {
            head.appendChild(nodes[i]);
			//控制檯日誌部分(不須要能夠刪除)
			//start
            var url = pendingUrls[i];
            if (/.js/.exec(url)) {
                console.log((i + 1) + "--> js成功: " + url);
            } else if (/.css/.exec(url)) {
                console.log((i + 1) + "--> css成功: " + url);
            } else {
                console.log("error: " + url);
            }
            //end
        }
        
		//結束計時
        endTime = new Date().getTime();
		//控制檯日誌部分(不須要能夠刪除)
		//(startTime (在此方法開頭),endTime(在此行代碼上方) 均可以刪除 )
        console.log("執行時間: " + (endTime - startTime) + " ms ------- end時間戳:" + endTime);
    }

    /** 開始輪詢,以肯定指定的樣式表什麼時候完成加載 輪詢在加載全部掛起的樣式表或加載10個樣式表以後中止 秒(防止停頓)。 @method pollGecko @param {HTMLElement} node 樣式節點。 @private */
    function pollGecko(node) {
        var hasRules;

        try {
            //咱們不須要存儲這個值,也不須要再次引用它,可是
            //若是咱們不存儲它,閉包編譯器會認爲代碼是無用的
            //刪除它。
            hasRules = !!node.sheet.cssRules;
        } catch (ex) {
            // 異常意味着樣式表仍在加載。
            pollCount += 1;

            if (pollCount < 200) {
                setTimeout(function () { pollGecko(node); }, 50);
            } else {
                //咱們已經輪詢了10秒鐘,什麼都沒有發生。
                //中止輪詢並完成掛起的請求,以免進一步阻塞請求。
                hasRules && finish('css');
            }

            return;
        }
        
        // 到這裏,樣式表已經加載。
        finish('css');
    }

    /** 開始輪詢,以肯定掛起的樣式表什麼時候完成加載 在WebKit。輪詢在加載全部掛起的樣式表或加載10個樣式表以後中止 秒(防止停頓)。 @method pollWebKit @private */
    function pollWebKit() {
        var css = pending.css, i;

        if (css) {
            i = styleSheets.length;

            // 查找與掛起的URL匹配的樣式表。
            while (--i >= 0) {
                if (styleSheets[i].href === css.urls[0]) {
                    finish('css');
                    break;
                }
            }

            pollCount += 1;

            if (css) {
                if (pollCount < 200) {
                    setTimeout(pollWebKit, 50);
                } else {
                    //咱們已經輪詢了10秒鐘,什麼都沒有發生
                    //表示樣式表已從文檔中刪除
                    //在它有機會裝載以前。中止輪詢並完成掛起
                    //請求以防止阻止進一步的請求。
                    finish('css');
                }
            }
        }
    }

    // Public Methods --------------------------------------------------------
    return {
        /** 請求指定的CSS URL或URL並執行指定的 當它們完成加載時回調(若是有的話)。若是一個url數組是 指定後,樣式表將與回調並行加載 將在全部樣式表加載完成後執行。 @method css @param {String|Array} urls CSS URL或要加載的CSS URL數組 @param {Function} callback (optional) 回調函數 @param {Object} obj (optional) 對象傳遞給回調函數 @param {Object} context (optional) 若是提供,回調函數將在這個對象的上下文中執行嗎 @static */
        css: function (urls, callback, obj, context) {
            load('css', urls, callback, obj, context);
        },

        /** 請求指定的JavaScript URL並執行指定的 當它們完成加載時回調(若是有的話)。若是一個url數組是 指定並獲得瀏覽器的支持後,腳本將被加載進來 並將在全部腳本完成後執行回調 完成加載。 目前,只有Firefox和Opera支持同時並行加載腳本 保存執行順序。在其餘瀏覽器中,腳本將是 排隊並一次加載一個,以確保正確的執行順序 @method js @param {String|Array} urls JS URL或要加載的JS URL數組 @param {Function} callback (optional) 回調函數 @param {Object} obj (optional) 對象傳遞給回調函數 @param {Object} context (optional) 若是提供,回調函數將在這個對象的上下文中執行嗎 @static */
        js: function (urls, callback, obj, context) {
            load('js', urls, callback, obj, context);
        }
    };
})(this.document);
相關文章
相關標籤/搜索