LABjs详解:按需动态加载JavaScript文件的策略与实现

作者:安全边界时间:2026-10-29 09:28:07浏览:437

本文深入解析LABjs库如何优化页面性能,通过动态加载机制避免冗余脚本。详细对比Script Element、XHR Injection及Cache Trick三种加载技巧,分析其在不同浏览器中的行为差异。结合伪代码与实例,阐述LABjs如何根据浏览器特性智能选择预加载策略,确保脚本并行下载与顺序执行的平衡。

LABjs 概述与核心价值

LABjs 是一个轻量级的 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 加载方式

LABjs 中的动态加载脚本文件,指的是在页面 JavaScript 脚本执行过程中,通过多种技术手段加载外部 JS 文件。这主要区别于 HTML 页面中通过 <script> 标签进行的静态加载方式。

动态加载脚本的方式多种多样,各有优劣,此处不再赘述,有兴趣的读者可参考本文末尾的链接。

LABjs 主要运用了三种加载技巧,分别是 Script Element、XHR Injection 以及 Cache Trick。

首先对这三种加载方式进行简要介绍,随后在第四部分分析 LABjs 源码中针对不同场景的具体应用。

Script Element 加载方式

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

这是最常见的脚本动态加载方式,具有诸多优点,包括:1、实现简单;2、支持跨域;3、不会阻塞其他资源的加载等。

在 Opera 和旧版 Firefox 浏览器中,脚本执行的顺序与节点被插入页面的顺序一致。

在 IE、Safari 和 Chrome 浏览器中,执行顺序无法得到保证。

注意:

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

在旧版 Chrome 浏览器中,脚本执行的顺序与插入页面的顺序不一定一致,但可以通过将 script 标签的 async 属性设置为 false 来保证顺序执行。

XHR Injection 与 Cache Trick

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 里关于脚本加载采用方案的判断

忽略技术细节,通过一段伪代码来描述 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 的作者是不是没有想到这一点呢?

作者最新文章
相关文章
更多