51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

查看: 1645|回复: 3
打印 上一主题 下一主题

非阻塞JavaScript脚本加载

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2018-4-3 16:33:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
昨天在看《高性能JavaScript》,提到Javascript是以阻塞的方式加载的,也就是说:当JavaScript 运行时其他
的事情不能被浏览器处理。根据Yahoo的建议,脚本放在最后。

让网页先显示,先得到一个正确的外观,再加载脚本,为网页添加各种功能,锦上添花,是一个很好的实践。
以非阻塞的方式加载Javascript,就是这样的实践。其原理是使用一个脚本来加载其它脚本,这个脚本就是loa
der(或者其它你喜欢的名称)。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代码如下
  1. 非阻塞JS文件加载, 对CSS文件也可以加载.
  2. * 文件加载顺序:
  3. *   1. 最先加载CSS文件,
  4. *   2. 然后加载所有同步的JS文件(并且按add方法添加的顺序),
  5. *   3. 最后加载所有非同步的JS文件(不一定按add方法添加的顺序).
  6. * 加载CSS文件方法: loader.addcss('path/to/file.css', 'screen print');
  7. * 加载同步JS文件方法: loader.add('path/to/file.js', true[, callback[, callback_args]]);
  8. * 加载非同步JS文件方法: loader.add('path/to/file.js'[, false[, callback[, callback_args]]]);
  9. * 如果JS文件之间有依赖关系, 应当作为同步文件加载, 且按顺序调用add方法, 否则, 应以非同步文件加载
  10. */
  11. (function () {
  12.    
  13.     var loader = {},
  14.     _version = '0.0.1',
  15.     syncjs = [], // 同步的JS
  16.     asyncjs = [], // 非同步的JS
  17.     css = [], // CSS
  18.    
  19.     // 加载CSS文件
  20.     _loadcss = function () {
  21.         var head = document.getElementsByTagName('head')[0];
  22.         for (var i = 0, l = css.length; i < l; i++) {
  23.             var c = document.createElement('link');
  24.             c.type = 'text/css';
  25.             c.rel = 'stylesheet';
  26.             c.href = css[i].src;
  27.             c.media = css[i].media;
  28.             head.appendChild(c);
  29.         }
  30.         css = [];
  31.     },
  32.    
  33.     // JS回调函数
  34.     // f 回调函数, 或包含回调函数的数组
  35.     // a 回调函数的参数
  36.     // o JS Element 对象
  37.     _onload = function (f, a, o) {
  38.         if (o) {
  39.             if (typeof f === 'function') {
  40.                 f = [f];
  41.             }
  42.             if (o.readyState) {
  43.                 o.onreadystatechange = function () {
  44.                     if (o.readyState == 'loaded' || o.readyState == 'complete') {
  45.                         o.onreadystatechange = null;
  46.                         for (var i = 0, l = f.length; i < l; i++) {
  47.                             f[i](a);
  48.                         }
  49.                     }
  50.                 }
  51.             } else {
  52.                 o.onload = function () {
  53.                     for (var i = 0, l = f.length; i < l; i++) {
  54.                         f[i](a);
  55.                     }
  56.                 }
  57.             }
  58.         }
  59.     },
  60.    
  61.     // o 用add方法增加的JS对象
  62.     // p JS Script标签的父元素
  63.     // f 回调函数
  64.     _loadjs = function (o, p, f) {
  65.         var fs = [], js = document.createElement('script');
  66.         js.type = 'text/javascript';
  67.         if (typeof o.callback === 'function') {
  68.             fs.push(o.callback);
  69.         }
  70.         if (typeof f === 'function') {
  71.             fs.push(f);
  72.         }
  73.         if (fs.length > 0) {
  74.             _onload(fs, o.args, js);
  75.         }
  76.         js.src = o.src;
  77.         p.appendChild(js);
  78.     },
  79.    
  80.     // 加载同步的JS文件
  81.     _loadsyndjs = function () {
  82.         if (syncjs.length > 0) {
  83.             var head = document.getElementsByTagName('head')[0],
  84.                 js = syncjs.shift();
  85.                 _loadjs(js, head, _loadsyndjs);
  86.         } else {
  87.             _loadasyncjs();
  88.         }
  89.     },
  90.    
  91.     // 加载非同步的JS文件
  92.     _loadasyncjs = function () {
  93.         var head = document.getElementsByTagName('head')[0];
  94.         for (var i = 0, l = asyncjs.length; i < l; i++) {
  95.             _loadjs(asyncjs[i], head);
  96.         }
  97.         asyncjs = [];
  98.     };
  99.    
  100.     /**
  101.      * JS或CSS文件是否已经包含了
  102.      */
  103.     syncjs.has = asyncjs.has = css.has = function (v) {
  104.         for (var i = 0, l = this.length; i < l; i++) {
  105.             if (v == this[i].src) return true;
  106.         }
  107.         return false;
  108.     }
  109.    
  110.     /**
  111.      * 加载JS
  112.      * @param u JS路径
  113.      * @param sync 是否同步
  114.      * @param callback 加载完成后的回调函数
  115.      * @param args 回调函数的参数
  116.      */
  117.     loader.add = function (u, sync, callback, args) {
  118.         var js = sync ? syncjs : asyncjs;
  119.         if (!js.has(u)) {
  120.             js.push({'src': u, 'callback': callback, 'args': args});
  121.         }
  122.         return this;
  123.     };
  124.    
  125.     /**
  126.      * 加载CSS
  127.      * @param u CSS路径
  128.      * @param media CSS毁林类型
  129.      */
  130.     loader.addcss = function (u, media) {
  131.         if (!css.has(u)) {
  132.             css.push({'src': u, 'media': media || 'screen'});
  133.         }
  134.         return this;
  135.     };
  136.    
  137.     /**
  138.      * 开始加载
  139.      */
  140.     loader.load = function () {
  141.         _loadcss();
  142.         _loadsyndjs();
  143.     };
  144.    
  145.     /**
  146.      * 版本
  147.      */
  148.     loader.version = function () {
  149.         return _version;
  150.     };
  151.    
  152.     window.loader = loader;
  153. })(window);
复制代码
  1. <!DOCTYPE html>
  2. <html>
  3.     <head>
  4.         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  5.         <title>非阻塞脚本加载测试</title>
  6.         
  7.     </head>
  8.     <body>
  9.         <div>TODO write content</div>
  10.         <script type="text/javascript" src="../loader.js"></script>
  11.         <script type="text/javascript">
  12.             loader.add('test.js').add('../vendor/jquery-1.4.1.js', true, function (s) {
  13.                 $('body').append('<p>jQuery loaded</p><p>' + s + '</p>');
  14.             }, 'param').add('test.js').add('test2.js', true, function() {
  15.                 $('body').append('<p>' + loader.version() + '</p>');
  16.             }).addcss('http://www.cnblogs.com/css/reset.css').load();
  17.         </script>
  18.     </body>
  19. </html>
复制代码


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏
回复

使用道具 举报

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

GMT+8, 2024-5-28 14:28 , Processed in 0.062790 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2024 Comsenz Inc.

快速回复 返回顶部 返回列表