loadScript异步加载脚本示例讲解,非阻塞脚本的使
分类:关于美高梅

好了开始吧
重构这个其实也不是什么大动作,主要要实现的功能嘛,就是把现有的JS代码重新划分一下,解耦现有模块。然后我打算把现有的程序划分一下模块然后重新打包做一个命名空间实现use或者类似于java的Package的东西。那么我只要加载一个use的js文件调用这个文件的use函数,通过设置某些参数,我可以动态地加载所需要的模块。这个是最完美的想法(那时我很傻很天真)。好的,噩梦开始了。
前提,我低估了3个月前的自己。//好吧,下面可能会出现un文明用语~~

一、语法: loadScript(url[,callback])
或者
loadScript(settings)
二、settings支持的参数: url:脚本路径
async:是否异步,默认false(HTML5)
charset:文件编码
cache:是否缓存,默认为true
success:加载成功后执行的函数,优先执行callback。
三、调用举例:

JavaScript 倾向于阻塞浏览器某些处理过程,如HTTP 请求和界面刷新,这是开发者面临的最显著的性能问题。保持JavaScript文件短小,并限制HTTP请求的数量,只是创建反应迅速的网页应用的第一 步。一个应用程序所包含的功能越多,所需要的JavaScript 代码就越大,保持源码短小并不总是一种选择。尽管下载一个大JavaScript 文件只产生一次HTTP 请求,却会锁定浏览器一大段时间。为避开这种情况,你需要向页面中逐步添加JavaScript,某种程度上说不会阻塞浏览器。非阻塞脚本的秘密在于,等 页面完成加载之后,再加载JavaScript 源码。从技术角度讲,这意味着在window 的load 事件发出之后开始下载代码。有三种方法可以实现这种效果。
      1、延期脚本
      HTML 4 为<script>标签定义了一个扩展属性:defer。这个defer 属性指明元素中所包含的脚本不打算修改DOM,因此代码可以稍后执行。defer 属性只被Internet Explorer 4 和Firefox 3.5 更高版本的浏览器所支持,它不是一个理想的跨浏览器解决方案。在其他浏览器上,defer 属性被忽略,<script>标签按照默认方式被处理(造成阻塞)。如果浏览器支持的话,这种方法仍是一种有用的解决方案。示例如下:
      <script type="text/javascript" src="file1.js" defer></script>
      一个带有defer 属性的<script>标签可以放置在文档的任何位置。对应的JavaScript 文件将在<script>被解析时启动下载,但代码不会被执行,直到DOM 加载完成

首先,计划的第一天,我的打算是分离这个程序里面最需要解耦的部分,自制控件部分。说起来,人家也尝试着写了一些窗体控件什么的比如Panel.js,Button.js之类的控件这里面有一大堆js文件,虽然我已经划分好文件夹了,但是看到index页面上面那一连串的<script>标签,各种蛋疼的说。于是噩梦进入第二阶段,我想加载一个JS文件,而这个JS文件可以动态地加载所有的控件JS。如果想了解"动态加载JS"的相关知识,请去度娘G娘那里问个明白。我想应该会搜到好多3异步+1Ajax的实现。好了,这些都是废话,参考了《高性能JavaScript》一书,产生以下代码:

复制代码 代码如下:

(在onload 事件句柄被调用之前)。当一个defer的JavaScript 文件被下载时,它不会阻塞浏览器的其他处理过程,所以这些文件可以与页面的其他资源一起并行下载。任何带有defer 属性的<script>元素在DOM 加载完成之前不会被执行,不论是内联脚本还是外部脚本文件,都是这样。下面的例子展示了defer 属性如何影响脚本行为:
      <html>
            <head>
                  <title>Script Defer Example</title>
            </head>
            <body>
                  <script defer>
                        alert("defer");
                  </script>
                  <script>
                        alert("script");
                  </script>
                  <script>
                        window.onload = function(){
                  alert("load");
                  };
                  </script>
            </body>
      </html>
      这些代码在页面处理过程中弹出三个对话框。如果浏览器不支持defer 属性,那么弹出对话框的顺序是“defer”,“script”和“load”。如果浏览器支持defer 属性,那么弹出对话框的顺序是“script”,“defer”和“load”。注意,标记为defer 的<script>元素不是跟在第二个后面运行,而是在onload 事件句柄处理之前被调用。如果你的目标浏览器只包括Internet Explorer 和Firefox 3.5,那么defer 脚本确实有用。如果你需要支持跨领域的多种浏览器,那么还有更一致的实现方式。
      2、动态脚本元素
      文档对象模型(DOM)允许你使用JavaScript 动态创建HTML 的几乎全部文档内容。其根本在于,<script>元素与页面其他元素没有什么不同:引用变量可以通过DOM 进行检索,可以从文档中移动、删除,也可以被创建。一个新的<script>元素可以非常容易地通过标准DOM 函数创建:
      var script = document.createElement ("script");
      script.type = "text/javascript";
      script.src = "file1.js";
      document.getElementsByTagName_r("head")[0].appendChild(script);
      新的<script>元素加载file1.js 源文件。此文件当元素添加到页面之后立刻开始下载。此技术的重点在于:无论在何处启动下载,文件的下载和运行都不会阻塞其他页面处理过程。你甚至可以将这 些代码放在<head>部分而不会对其余部分的页面代码造成影响(除了用于下载文件的HTTP 连接)。
      当文件使用动态脚本节点下载时,返回的代码通常立即执行(除了Firefox 和Opera,他们将等待此前的所有动态脚本节点执行完毕)。当脚本是“自运行”类型时这一机制运行正常,但是如果脚本只包含供页面其他脚本调用调用的接 口,则会带来问题。这种情况下,你需要跟踪脚本下载完成并准备妥善的情况。可以使用动态<script>节点发出事件得到相关信息。
      Firefox, Opera, Chorme 和Safari 3+会在<script>节点接收完成之后发出一个load 事件。你可以监听这一事件,以得到脚本准备好的通知:
      var script = document.createElement ("script")
      script.type = "text/javascript";
      //Firefox, Opera, Chrome, Safari 3+
            script.onload = function(){
            alert("Script loaded!");
      };
      script.src = "file1.js";
      document.getElementsByTagName("head")[0].appendChild(script);
      Internet Explorer 支持另一种实现方式,它发出一个readystatechange 事件。<script>元素有一个readyState
属性,它的值随着下载外部文件的过程而改变。readyState 有五种取值:
      “uninitialized”默认状态
      “loading”下载开始
      “loaded”下载完成
      “interactive”下载完成但尚不可用
      “complete”所有数据已经准备好
      微软文档上说,在<script>元素的生命周期中,readyState 的这些取值不一定全部出现,但并没有指出哪些取值总会被用到。实践中,我们最感兴趣的是“loaded”和“complete”状态。Internet Explorer 对这两个readyState 值所表示的最终状态并不一致,有时<script>元素会得到“loader”却从不出现“complete”,但另外一些情况下出现 “complete”而用不到“loaded”。最安全的办法就是在readystatechange 事件中检查这两种状态,并且当其中一种状态出现时,删除readystatechange 事件句柄(保证事件不会被处理两次):
      var script = document.createElement("script")
      script.type = "text/javascript";
      //Internet Explorer
      script.onreadystatechange = function(){
            if (script.readyState == "loaded" || script.readyState == "complete"){
                  script.onreadystatechange = null;
                  alert("Script loaded.");
            }
      };
      script.src = "file1.js";
      document.getElementsByTagName("head")[0].appendChild(script);
      大多数情况下,你希望调用一个函数就可以实现JavaScript 文件的动态加载。下面的函数封装了标准实现和IE 实现所需的功能:
      function loadScript(url, callback){
            var script = document.createElement ("script")
            script.type = "text/javascript";
            if (script.readyState){ //IE
                  script.onreadystatechange = function(){
                        if (script.readyState == "loaded" || script.readyState == "complete"){
                              script.onreadystatechange = null;
                              callback();
                        }
                 };
            } else { //Others
                  script.onload = function(){
                  callback();
            };
      }
      script.src = url;
      document.getElementsByTagName("head")[0].appendChild(script);
      }
      此函数接收两个参数:JavaScript 文件的URL,和一个当JavaScript 接收完成时触发的回调函数。属性检查用于决定监视哪种事件。最后一步,设置src 属性,并将<script>元素添加至页面。此loadScript()函数使用方法如下:
      loadScript("file1.js", function(){
            alert("File is loaded!");
      });
      你可以在页面中动态加载很多JavaScript 文件,但要注意,浏览器不保证文件加载的顺序。所有主流浏览器之中,只有Firefox 和Opera 保证脚本按照你指定的顺序执行。其他浏览器将按照服务器返回它们的次序下载并运行不同的代码文件。你可以将下载操作串联在一起以保证他们的次序,如下:
      loadScript("file1.js", function(){
            loadScript("file2.js", function(){
                  loadScript("file3.js", function(){
                        alert("All files are loaded!");
                  });
            });
      });
      此代码等待file1.js 可用之后才开始加载file2.js,等file2.js 可用之后才开始加载file3.js。虽然此方法可
行,但如果要下载和执行的文件很多,还是有些麻烦。如果多个文件的次序十分重要,更好的办法是将这些文件按照正确的次序连接成一个文件。独立文件可以一次性下载所有代码(由于这是异步进行的,使用一个大文件并没有什么损失)。
动态脚本加载是非阻塞JavaScript 下载中最常用的模式,因为它可以跨浏览器,而且简单易用。
      3、使用XMLHttpRequest(XHR)对象
      此技术首先创建一个XHR 对象,然后下载JavaScript 文件,接着用一个动态<script>元素将JavaScript 代码注入页面。下面是一个简单的例子:
      var xhr = new XMLHttpRequest();
      xhr.open("get", "file1.js", true);
      xhr.onreadystatechange = function(){
            if (xhr.readyState == 4){
                  if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
                        var script = document.createElement ("script");
                        script.type = "text/javascript";
                        script.text = xhr.responseText;
                        document.body.appendChild(script);
                  }
            }
      };
      xhr.send(null);
此代码向服务器发送一个获取file1.js 文件的GET 请求。onreadystatechange 事件处理函数检查readyState是不是4,然后检查HTTP 状态码是不是有效(2XX 表示有效的回应,304 表示一个缓存响应)。如果收到了一个有效的响应,那么就创建一个新的<script>元素,将它的文本属性设置为从服务器接收到的 responseText 字符串。这样做实际上会创建一个带有内联代码的<script>元素。一旦新<script>元素被添加到文档,代码将被执行, 并准备使用。这种方法的主要优点是,你可以下载不立即执行的JavaScript 代码。由于代码返回在<script>标签之外(换句话说不受<script>标签约束),它下载后不会自动执行,这使得你可以 推迟执行,直到一切都准备好了。另一个优点是,同样的代码在所有现代浏览器中都不会引发异常。此方法最主要的限制是:JavaScript 文件必须与页面放置在同一个域内,不能从CDNs 下载(CDN 指“内容投递网络(Content Delivery Network)”,大型网页通常不采用XHR 脚本注入技术。
      推荐的向页面加载大量JavaScript 的方法分为两个步骤:第一步,包含动态加载JavaScript 所需的代码,然后加载页面初始化所需的除JavaScript 之外的部分。这部分代码尽量小,可能只包含loadScript()函数,它下载和运行非常迅速,不会对页面造成很大干扰。当初始代码准备好之后,用它来 加载其余的JavaScript。例如:
      <script type="text/javascript" src="loader.js"></script>
      <script type="text/javascript">
            loadScript("the-rest.js", function(){
                  Application.init();
            });
      </script>
      将此代码放置在body 的关闭标签</body>之前。这样做有几点好处:首先,像前面讨论过的那样,这样做确保JavaScript 运行不会影响页面其他部分显示。其次,当第二部分JavaScript 文件完成下载,所有应用程序所必须的DOM 已经创建好了,并做好被访问的准备,避免使用额外的事件处理(例如window.onload)来得知页面是否已经准备好了。另一个选择是直接将 loadScript()函数嵌入在页面中,这可以避免另一次HTTP 请求。例如:
      <script type="text/javascript">
            function loadScript(url, callback){
                  var script = document.createElement ("script")
                  script.type = "text/javascript";
                  if (script.readyState){ //IE
                        script.onreadystatechange = function(){
                              if (script.readyState == "loaded" ||
                              script.readyState == "complete"){
                                    script.onreadystatechange = null;
                                    callback();
                              }
                        };
                  } else { //Others
                        script.onload = function(){
                        callback();
                        };
                  }
                  script.src = url;
                  document.getElementsByTagName_r("head")[0].appendChild(script);
            }
            loadScript("the-rest.js", function(){
                  Application.init();
            });
      </script>
      如果你决定使用这种方法,建议你使用“YUI Compressor”(参见第9 章)或者类似的工具将初始化脚本缩小到最小字节尺寸。一旦页面初始化代码下载完成,你还可以使用loadScript()函数加载页面所需的额外功能函数。

复制代码 代码如下:

//loadScript(url[,callback])
loadScript(“);
loadScript(“
console.log(1)
});
//loadScript(settings)
loadScript({“url”:”);
loadScript({“url”:”
console.log(2)
}});
//或者你可以酱紫:
//loadScript(settings[,callback])
loadScript({“url”:”
console.log($)
});

转载自
本文链接地址:

function loadScript (url, callback){
var script = document.createElement("script");
script.type = "text/javascript";
if(script.readyState){ //IE
script.onreadystatechange = function(){
if(script.readyState == "loaded" || script.readState == "complete"){
script.onreadystatechange = null;
callback();
}
}
}else{
script.onload = function(){
callback();
}
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}

四、源代码:

好的悲剧慢慢开始,首先我的控件都是基于JQuery的那么必然要 loadScript(jqueryURL, function(){//加载我的控件s}),好的这里说到这里打断一下,下面再接上。
然后我又突发奇想要做命名空间的功能,好的研究了面向对象啊,原型链啊之类杂七杂八的东西然后发现这种打点引用的功能好抽象,给那本《javascript设计模式》的书忽悠的七零八落。最后在了解了原型模式之后,还是一团迷雾。好的,我觉得我要重新思考这个问题,我其实只是想要打点出控件而已,那么我只要将我的控件作为一个对象的属性绑定到一个全局的对象上面就好了。于是我用了自己的英文名Gssl作为一个对象得出如下结构:

复制代码 代码如下:

复制代码 代码如下:

function loadScript(url,callback) {
var head = document.head || document.getElementsByTagName(“head”)[0] || document.documentElement,
script,
options,

var Gssl = {}

if (typeof url === “object”) {
options = url;
url = undefined;
}
s = options || {};
url = url || s.url;
callback = callback || s.success;
script = document.createElement(“script”);
script.async = s.async || false;
script.type = “text/javascript”;
if (s.charset) {
script.charset = s.charset;
}
if(s.cache === false){
url = url+( /?/.test( url ) ? “&” : “?” )+ “_=” +(new Date()).getTime();
}
script.src = url;
head.insertBefore(script, head.firstChild);
if(callback){
document.addEventListener ? script.addEventListener(“load”, callback, false) : script.onreadystatechange = function() {
if (/loaded|complete/.test(script.readyState)) {
script.onreadystatechange = null
callback()
}
}
}
}

好了回到上面打断的地方,我的想法就是在动态加载JS的时候顺便构造我的全局对象并绑定到空间名为Gssl下,具体实现如下:

您可能感兴趣的文章:

  • JS动态加载脚本并执行回调操作
  • 跟我学习javascript的异步脚本加载
  • 使用jQuery动态加载js脚本文件的方法
  • JS 动态加载脚本的4种方法
  • 怎么判断js脚本加载完成
  • js 判断脚本加载完毕的代码
  • 动态加载Js代码到Head标签中的脚本
  • js实现动态加载脚本的方法实例汇总
  • 编写高性能的JavaScript 脚本的加载与执行
  • 5种JavaScript脚本加载的方式

复制代码 代码如下:

loadScript(jqueryURL, function(){
//加载我的控件s
loadScript(controlURL, function(){
//绑定控件
Gssl.control = control;
});
});

写到这里,测试是调通了,昨天晚上,小开心了一下,但是程序员的直觉话我知,噩梦还没有结束。
今天早上回去把这个动态加载JS的JS文件引用到了我的页面那里,结果因为异步的特点,后面的代码没有等到这个Gssl的对象生成完成就开始执行了(我去,这不合理啊)。然后思考了一下,想在最后加载的一个控件那里做一个ready标志位以标志Gssl到底有没有加载完成。但是发现每个组件各自有各自的callback函数,你根本就不知道哪一个才是最后加载的,虽然代码执行是有顺序的,但是这个传输的并行性又让你不能确定到底哪一个才是最后一个。好的我彻底崩溃了,于是想了一个非常2B的方法,干脆写一个函数来卡住程序2秒吧,两秒肯定可以了~。然后发现setTimeout TM不能卡代码的,他的好基友setIXXXXX也是不能卡代码的。好的,朕生气了,写了一个死循环循环判断ready位。好的,浏览器不干了。

回到原点,我开始考虑尝试递归式的加载就是在Callback的时候才去加载下一个控件,这样我就能知道控件什么时候加载完了。但是仔细一想,我擦,如果要这样加载那么我还动态加载个屁啊,这不就一点也没有提高到效率么。然后看了各种框架的ready方法的实现。嗯 TM单文件的就是IMBA啊。那么摆在我面前的就只有一条路了,把所有的控件都写在一个JS上面。这样根本就是避重就轻啊。

然后我就不断在这种提出解决方案,然后不断自我吐槽中度过了噩梦般的一天。快下班了,我还在不停地思考这个问题究竟有没有解。然后脑里面第三个声音开始了,志伟啊~(呵呵本人的名字就是这个了~),真的有必要么?好的,不得不承认,每次脱离噩梦就得靠他。然后我把整个项目的文件夹打开每层每层地点开又退回去,然后思考,好吧,不是写小说,这些思考时候的小动作就不描述了(我会告诉你我想问题的时候会好像精神病人一样犯傻么)。最后我发现就算我把这些模块都抽离了,去到其他的项目还是要做出一定的修改,虽然有做接口,但是接口是接后台的,我模块间的接口还没有做。这样的抽离会伴随着一大堆额外的支付(估计的啦,但是根据经验这些是必然的~),并且新的JS框架在整体框架里面并不兼容(下班的时候发现某些资源访问出问题了),虽然不死心,但是还是放弃了(万恶的进度,次奥)。这一版的代码也没有做保存,呃SVN也没有更新上去~。我的U盘移硬上面也没有备份,但是所有的源码都给我一怒之下付诸Delete了。仅以此篇日志留作纪念。

教训就是如果一开始我就有一个前端模块化的思想,就不会走到今天这一步了。以我的能力完全可以做到,但是现在已经积重难返了~还是那些万恶的奶粉广告,让宝宝有个好的开始,那么我的就是让代码有个好的开始吧~原谅我这个不及格的粑粑~~ (T_T)

另外我知道博客园是个神奇的地方,如果有同人遇到相同的困扰并且切实解决了的话,可否分享一下呢?有回必复!

您可能感兴趣的文章:

  • js动态调用css属性的小规律及实例说明
  • JavaScript 错误处理与调试经验总结
  • js下关于onmouseout、事件冒泡的问题经验小结
  • 写给想学习Javascript的朋友一点学习经验小结
  • javascript 操作select下拉列表框的一点小经验
  • JS效率个人经验谈(8-15更新),加入range技巧
  • javascript 框架小结 个人工作经验
  • jquery.validate.js插件使用经验记录
  • Javascript 多浏览器兼容总结(实战经验)
  • 使用node.js半年来总结的 10 条经验
  • javascript 构造函数强制调用经验总结
  • 【经验总结】编写JavaScript代码时应遵循的14条规律

本文由美高梅网址发布于关于美高梅,转载请注明出处:loadScript异步加载脚本示例讲解,非阻塞脚本的使

上一篇:自制滚动条的小例子,js实现拖拽效果 下一篇:没有了
猜你喜欢
热门排行
精彩图文