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);

转载于:https://www.cnblogs.com/xxxxue/p/11153597.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值