详谈LABJS按需动态加载js文件


Posted in Javascript onMay 07, 2015

LABjs 是一个很小的 JavaScript 工具,用来根据需要加载 JavaScript 文件,通过使用该工具可以提升页面的性能,避免加载不需用到的 JavaScript 文件,可以实现动态并行加载脚本文件,以及管理加载脚本文件的执行顺序。

简单示例

$LAB
.script("script1.js", "script2.js", "script3.js")
.block(function(){
  // wait for all to load, then do something
  script1Func();
  script2Func();
  script3Func();
});

介绍下LABJS的几个实例:
实例1:

$LAB
  .script("script1.js")
  .script("script2.js")
  .script("script3.js")
  .wait(function(){ // 等待所有script加载完再执行这个代码块
    script1Func();
    script2Func();
    script3Func();
  });

实例2:

$LAB 
  .script({ src: "script1.js", type: "text/javascript" })
  .script("script2.js")
  .script("script3.js")
  .wait(function(){ // 等待所有script加载完再执行这个代码块
    script1Func();
    script2Func();
    script3Func();
  });

实例3:

$LAB
  .script("script1.js", "script2.js", "script3.js")
  .wait(function(){ // 等待所有script加载完再执行这个代码块
    script1Func();
    script2Func();
    script3Func();
  });

实例4:

$LAB
  .script( [ "script1.js", "script2.js" ], "script3.js")
  .wait(function(){ // 等待所有script加载完再执行这个代码块
    script1Func();
    script2Func();
    script3Func();
  });

实例5:

$LAB
  .script("script1.js").wait() // 空的wait()只是确保script1在其他代码之前被执行
  .script("script2.js") // script2 和 script3 依赖于 script1
  .script("script3.js").wait() // 但是script2 和 script3 并不互相依赖,可以并行下载
  .script("script4.js") // script4 依赖于 script1, script2 及 script3
  .wait(function(){script4Func();});

实例6:

$LAB
  .script("script1.js") // script1, script2, and script3 之间没有依赖关系,
  .script("script2.js") // 所以可以任意顺序执行
  .script("script3.js")
  .wait(function(){ // 如果需要,这里当然可以执行javascript函数
    alert("Scripts 1-3 are loaded!");
  })
  .script("script4.js") // 依赖于 script1, script2 及 script3
  .wait(function(){script4Func();});

实例7:

$LAB
  .setOptions({AlwaysPreserveOrder:true}) // 设置每个脚本之间等待
  .script("script1.js") // script1, script2, script3, script4 互相依赖
  .script("script2.js") // 并且并行下载后循序执行
  .script("script3.js")
  .script("script4.js")
  .wait(function(){script4Func();});

实例8:

$LAB
  .script(function(){
    // `_is_IE`的值ie为true ,非ie为false
    if (_is_IE) {
      return "ie.js"; // 如果是ie则这个js会被加载
    }
    else {
      return null; //如果不是ie这个代码就会被略过
    }
  })
  .script("script1.js")
  .wait();

LABjs加载方式

LABjs里的动态加载脚本文件,是指页面的js脚本执行时,通过多种方法去加载外部的js(主要区别于html页面里,通过<script>标签静态加载的脚本)

动态加载脚本的方式有很多,优缺点不一,此处不赘述,有兴趣的童鞋可以参见本文末尾的参考链接 :)。

LABjs里主要使用了三种技巧,分别为Script Element、XHR Injection以及Cache Trick

首先对这三种加载方式进行简单介绍,第四部分再分析LABjs源码实现里面对着三种方式分别的使用场景

Script Element(LABjs默认采用加载方式)

最常见的脚本动态加载方式,优点很多,包括:1、实现简单 2、可跨域 3、不会阻塞其他资源的加载 等

Opera/Firefox(老版本)下:脚本执行的顺序与节点被插入页面的顺序一致

IE/Safari/Chrome下:执行顺序无法得到保证

注意:

新版本的Firefox下,脚本执行的顺序与插入页面的顺序不一定一致,但可通过将script标签的async属性设置为false来保证顺序执行

老版本的Chrome下,脚本执行的顺序与插入页面的顺序不一定一致,但可通过将script标签的async属性设置为false来保证顺序执行

XHR Injection
通过ajax请求加载脚本文件,然后再通过以下方式执行:
eval:常见方式
XHR injection:创建一个script元素,并将请加载的脚本文件的内容注入
主要限制:无法跨域
Cache Trick(强依赖于浏览器的特性实现,不推荐使用)
当你将script元素的type属性设置为浏览器不认识的值,比如”text/cache”、”text/casper”、”text/hellworld”等,不同浏览器的行为如下:
IE/Safari/Chrome(老版本)里:脚本照常加载,但不会执行,假设浏览器没有禁用缓存,加载后的脚本会被浏览器缓存起来,当需要用到 的时候,只需要重新创建个script标签,将type设为正确的值,src指向之前请求的文件url即可(相当于从缓存里读文件)
Opera/Firefox:不加载
备注:
强依赖于浏览器的特性实现,有可能随着浏览器特性实现的改变而失效,不推荐使用
新版本的chrome浏览器,将script元素的type设置为非”text/javascript”,不会再对脚本文件进行加载。

LABjs里关于脚本加载采用方案的判断

忽略技术细节,通过一段伪代码来描述LABjs里面的实现,大致为:
首先判断是否对请求的脚本进行预加载(是否进行预加载的判断条件看伪代码注释);
如进行预加载,再判断浏览器是否支持真正的预加载;如支持真正的预加载,则预加载之;如否,判断请求的脚本是否跟当前页面同域,如实,采用XHR Injection,如否,采用Cache Trick;
如不进行预加载,判断浏览器支不支持script元素的async属性(见伪代码注释),如是,设置async属性,并请求脚本文件;如否,直接通过script元素加载脚本文件;
 

if(ifPreloadScript){  //当请求的脚本文件是否进行预加载:1、需要预加载 2、浏览器支持预加载
  if(supportRealPreloading){  //如果支持真正的预加载
    if(supportPreloadPropNatively){  //支持通过设置script标签的preload属性,实现script的预加载,以及分离加载和执行
                    //Nicholas C. Zakas大神的美好愿望,尚未有浏览器支持:/blog/2011/02/14/separating-javascript-download-and-execution/
      script.onpreload = callback;
      script.newPreload = true;
      script.src = targetUrl;
    }else{
      script.onreadystatechange = callback;  //其实就是指IE浏览器,假设指定了script元素的src属性,IE浏览器里会立即加载
      script.src = targetUrl;  //即使script元素没有被插入页面,callback为预加载后的回调
    }
  }
  else if(inSameDomain){  //非跨域,采用XHR Injection:请求的脚本与当前页面处于同一个域
    xhr = new XMLHttpRequest();  //由于上个判断已经将IE无情地抛弃在这个条件分支之外,所以大胆地用 new XMLHttpRequest()吧
    xhr.onreadystatechange = callback;
    xhr.open("GET",targetUrl);
    xhr.send();
  }
  else{  //最无奈的后招,Cache Trick,新版chromei已经不支持
    script.onload = callback;
    script.type = 'text/cache';  
    script.src = targetUrl;
  }
}else{
  if(canContrlExecutionOrderByAsync){  //如果能够通过script元素的async属性来强制并行加载的脚本顺序执行
                    //kyle大神着力推进的提案,目前已被html5小组接受并放入草案:/Dynamic_Script_Execution_Order#My_Solution
    script.onload = callback;
    script.async = false;  //将script元素的async设为false,可以保证script的执行顺序与请求顺序保持一致
    script.src = targetUrl;
  }
  else{
    script.onload = callback;
    script.src = targetUrl;  
  }
}

实际上,当你在页面创建一个img节点,并将其src指向一个脚本文件,在部分浏览器里同样能够起到文件预加载的作用,那么LABjs的作者是不是没有想到这一点呢?

Javascript 相关文章推荐
自己做的模拟模态对话框实现代码
May 23 Javascript
详解JavaScript中this关键字的用法
May 26 Javascript
微信小程序 动态的设置图片的高度和宽度详解及实例代码
Feb 24 Javascript
JavaScript实现父子dom同时绑定两个点击事件,一个用捕获,一个用冒泡时执行顺序的方法
Mar 30 Javascript
使用use注册Vue全局组件和全局指令的方法
Mar 08 Javascript
JavaScript 跨域之POST实现方法
May 07 Javascript
微信小程序生成分享海报方法(附带二维码生成)
Mar 29 Javascript
微信小程序基于Taro的分享图片功能实践详解
Jul 12 Javascript
Node.js实现批量下载图片简单操作示例
Jan 18 Javascript
jquery更改元素属性attr()方法操作示例
May 22 jQuery
vue 组件基础知识总结
Jan 26 Vue.js
vue使用wavesurfer.js解决音频可视化播放问题
Apr 04 Vue.js
JavaScript改变CSS样式的方法汇总
May 07 #Javascript
js实现非常简单的焦点图切换特效实例
May 07 #Javascript
jQuery中closest和parents的区别分析
May 07 #Javascript
js实现键盘上下左右键选择文字并显示在文本框的方法
May 07 #Javascript
基于jQuery.Hz2Py.js插件实现的汉字转拼音特效
May 07 #Javascript
JavaScript更改字符串的大小写
May 07 #Javascript
JQUERY简单按钮轮换选中效果实现方法
May 07 #Javascript
You might like
一个简单计数器的源代码
2006/10/09 PHP
PHP通过COM使用ADODB的简单例子
2006/12/31 PHP
php SQL之where语句生成器
2009/03/24 PHP
php switch语句多个值匹配同一代码块的实现
2014/03/03 PHP
php计算两个日期时间差(返回年、月、日)
2014/06/19 PHP
PHP数组实例详解
2016/06/26 PHP
PHP排序算法之直接插入排序(Straight Insertion Sort)实例分析
2018/04/20 PHP
PHP内置函数生成随机数实例
2019/01/18 PHP
PHP单例模式实例分析【防继承,防克隆操作】
2019/05/22 PHP
PHP数组基本用法与知识点总结
2020/06/02 PHP
jQuery 学习入门篇附实例代码
2010/03/16 Javascript
JSQL 基于客户端的成绩统计实现方法
2010/05/05 Javascript
基于jquery中children()与find()的区别介绍
2013/04/26 Javascript
js中数组结合字符串实现查找(屏蔽广告判断url等)
2016/03/30 Javascript
Bootstrap中的表单验证插件bootstrapValidator使用方法整理(推荐)
2016/06/21 Javascript
vue.js实现请求数据的方法示例
2017/02/07 Javascript
jQuery插件Echarts实现的双轴图效果示例【附demo源码下载】
2017/03/04 Javascript
关于angular js_$watch监控属性和对象详解
2017/04/24 Javascript
浅谈redux以及react-redux简单实现
2018/08/28 Javascript
微信小程序文章详情页跳转案例详解
2019/07/09 Javascript
JavaScript实现轮播图效果代码实例
2019/09/28 Javascript
基于javascript canvas实现五子棋游戏
2020/07/08 Javascript
使用py2exe在Windows下将Python程序转为exe文件
2016/03/04 Python
Python安装使用命令行交互模块pexpect的基础教程
2016/05/12 Python
Python 给某个文件名添加时间戳的方法
2018/10/16 Python
Numpy之reshape()使用详解
2019/12/26 Python
Mysql数据库反向生成Django里面的models指令方式
2020/05/18 Python
python 列表推导和生成器表达式的使用
2021/02/01 Python
CSS3中Color的一些特性介绍
2012/05/27 HTML / CSS
校园十佳歌手策划书
2014/01/22 职场文书
乡镇交通安全实施方案
2014/03/29 职场文书
小学毕业寄语大全
2014/04/03 职场文书
诉讼财产保全担保书
2014/05/20 职场文书
感谢信怎么写
2015/01/21 职场文书
2019年图书室自查报告范本
2019/10/12 职场文书
Python matplotlib 利用随机函数生成变化图形
2022/04/26 Python