zoukankan      html  css  js  c++  java
  • 非阻塞JavaScript脚本加载

    昨天在看《高性能JavaScript》,提到Javascript是以阻塞的方式加载的,也就是说:当JavaScript 运行时其他的事情不能被浏览器处理。根据Yahoo的建议,脚本放在最后。

    让网页先显示,先得到一个正确的外观,再加载脚本,为网页添加各种功能,锦上添花,是一个很好的实践。以非阻塞的方式加载Javascript,就是这样的实践。其原理是使用一个脚本来加载其它脚本,这个脚本就是loader(或者其它你喜欢的名称)。loader最好放在最后,这样一个网页先显示出来,再通过loader加载其它功能脚本,如库、事件绑定等,可以提高网页的显示速度,提高网站的性能。

    书中推荐使用YUI、LazyLoad、LABjs来加载脚本,但我更喜欢自己的定制的方式,因此也写了一个Loader。

    这个Loader实现的功能有:

    1. 可以加载 CSS 和 JS 文件,并且先加载CSS文件(如果有的话)。

    2. JS文件有两种加载方式:同步与异步(默认)。这里所谓的同步,是指等待前一个JS文件加载完成后,再加载当前的JS文件,并且按loader.add的方法的调用顺序加载,执行的顺序与依赖关系有保障。如果一个JS文件被指定为异步加载,则它会在所有的同步文件加载完成后,再进行加载。所有异步加载的JS,不能保证它们能按顺序执行。

    3. 如果是加载JS文件,支持加载完成后的回调函数,这个回调函数可以接收一个参数。

    4. 通过add方法添加JS文件,addcss方法添加CSS文件,这时文件并未开始加载,最后通过load方法,才进行加载。如果文件在load方法之前已经通过add或addcss添加过,则不会重复加载。每次调用load方法后,清空add与addcss产生的缓存。

    5. 实现我喜欢的 loader.add('file1.js').add('file2.js').add('file3.js', true).addcss('css1.css').add('css2.css').load(); 风格,

    更详细的原理及其它的实现方式,用“非阻塞 javascript”来搜索一下园子里,就有好几篇文章,说得也很详细。

    Loader代码如下

    非阻塞脚本加载Loader
    /**
     * 非阻塞JS文件加载, 对CSS文件也可以加载.
     * 文件加载顺序:
     *   1. 最先加载CSS文件,
     *   2. 然后加载所有同步的JS文件(并且按add方法添加的顺序),
     *   3. 最后加载所有非同步的JS文件(不一定按add方法添加的顺序).
     * 加载CSS文件方法: loader.addcss('path/to/file.css', 'screen print');
     * 加载同步JS文件方法: loader.add('path/to/file.js', true[, callback[, callback_args]]);
     * 加载非同步JS文件方法: loader.add('path/to/file.js'[, false[, callback[, callback_args]]]);
     * 如果JS文件之间有依赖关系, 应当作为同步文件加载, 且按顺序调用add方法, 否则, 应以非同步文件加载
     
    */
    (function () {
        
        var loader = {},
        _version = '0.0.1',
        syncjs = [], // 同步的JS
        asyncjs = [], // 非同步的JS
        css = [], // CSS
        
        // 加载CSS文件
        _loadcss = function () {
            var head = document.getElementsByTagName('head')[0];
            for (var i = 0, l = css.length; i < l; i++) {
                var c = document.createElement('link');
                c.type = 'text/css';
                c.rel = 'stylesheet';
                c.href = css[i].src;
                c.media = css[i].media;
                head.appendChild(c);
            }
            css = [];
        },
        
        // JS回调函数
        // f 回调函数, 或包含回调函数的数组
        // a 回调函数的参数
        // o JS Element 对象
        _onload = function (f, a, o) {
            if (o) {
                if (typeof f === 'function') {
                    f = [f];
                }
                if (o.readyState) {
                    o.onreadystatechange = function () {
                        if (o.readyState == 'loaded' || o.readyState == 'complete') {
                            o.onreadystatechange = null;
                            for (var i = 0, l = f.length; i < l; i++) {
                                f[i](a);
                            }
                        }
                    }
                } else {
                    o.onload = function () {
                        for (var i = 0, l = f.length; i < l; i++) {
                            f[i](a);
                        }
                    }
                }
            }
        },
        
        // o 用add方法增加的JS对象
        // p JS Script标签的父元素
        // f 回调函数
        _loadjs = function (o, p, f) {
            var fs = [], js = document.createElement('script');
            js.type = 'text/javascript';
            if (typeof o.callback === 'function') {
                fs.push(o.callback);
            }
            if (typeof f === 'function') {
                fs.push(f);
            }
            if (fs.length > 0) {
                _onload(fs, o.args, js);
            }
            js.src = o.src;
            p.appendChild(js);
        },
        
        // 加载同步的JS文件
        _loadsyndjs = function () {
            if (syncjs.length > 0) {
                var head = document.getElementsByTagName('head')[0],
                    js = syncjs.shift();
                    _loadjs(js, head, _loadsyndjs);
            } else {
                _loadasyncjs();
            }
        },
        
        // 加载非同步的JS文件
        _loadasyncjs = function () {
            var head = document.getElementsByTagName('head')[0];
            for (var i = 0, l = asyncjs.length; i < l; i++) {
                _loadjs(asyncjs[i], head);
            }
            asyncjs = [];
        };
        
        /**
         * JS或CSS文件是否已经包含了
         
    */
        syncjs.has = asyncjs.has = css.has = function (v) {
            for (var i = 0, l = this.length; i < l; i++) {
                if (v == this[i].src) return true;
            }
            return false;
        }
        
        /**
         * 加载JS
         * @param u JS路径
         * @param sync 是否同步
         * @param callback 加载完成后的回调函数
         * @param args 回调函数的参数
         
    */
        loader.add = function (u, sync, callback, args) {
            var js = sync ? syncjs : asyncjs;
            if (!js.has(u)) {
                js.push({'src': u, 'callback': callback, 'args': args});
            }
            return this;
        };
        
        /**
         * 加载CSS
         * @param u CSS路径
         * @param media CSS毁林类型
         
    */
        loader.addcss = function (u, media) {
            if (!css.has(u)) {
                css.push({'src': u, 'media': media || 'screen'});
            }
            return this;
        };
        
        /**
         * 开始加载
         
    */
        loader.load = function () {
            _loadcss();
            _loadsyndjs();
        };
        
        /**
         * 版本
         
    */
        loader.version = function () {
            return _version;
        };
        
        window.loader = loader;
    })(window);

     示例代码如下

    非阻塞脚本加载示例代码
    <!DOCTYPE html>
    <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <title>非阻塞脚本加载测试</title>
            
        </head>
        <body>
            <div>TODO write content</div>
            <script type="text/javascript" src="../loader.js"></script>
            <script type="text/javascript">
                loader.add(
    'test.js').add('../vendor/jquery-1.4.1.js'truefunction (s) {
                    $(
    'body').append('<p>jQuery loaded</p><p>' + s + '</p>');
                }, 
    'param').add('test.js').add('test2.js'truefunction() {
                    $(
    'body').append('<p>' + loader.version() + '</p>');
                }).addcss(
    'http://www.cnblogs.com/css/reset.css').load();
            
    </script>
        </body>
    </html>

    源码下载:

    /Files/heiing/js-loader-src.zip

  • 相关阅读:
    使用 CountDownLatch 控制多个线程执行顺序
    define 与 inline
    虚函数 纯虚函数 抽象方法 接口
    [转]Android 超高仿微信图片选择器 图片该这么加载
    Android ImageView src与backgroud
    Android View绘制原理分析
    Android 5.0 Default SMS App以及运营商授权SMS App
    Android 5.0 双卡信息管理分析
    Android 5.1 AOSP 源码获取
    Android 5.0 Uicc框架分析
  • 原文地址:https://www.cnblogs.com/heiing/p/2262110.html
Copyright © 2011-2022 走看看