高性能网站优化-确保异步加载脚本时保持执行顺序

《高性能网站建设进阶指南》javascript

脚本若是按照常规方式加载,不只会阻塞页面中其余内容的下载,还会阻塞脚本后面全部元素的渲染。异步加载脚本能够避免这种阻塞现象,从而提升页面加载速度。可是性能的提高是要付出代价的。代码的异步执行可能会出现竞争状态。简单地说就是页面内部的脚本须要的标示符若是是在外部文件中定义的,而当外部文件异步加载的时候,若是没有保证外部文件和内部脚本执行顺序,颇有可能会出现未定义标示符的错误php

当异步加载的外部脚本与行内脚本之间存在代码依赖时,就须要经过一种保证执行顺序的方法来整合这两个脚本。css

如何保证执行顺序

当外部脚本按常规方式加载时,他会阻塞行内代码的执行,不会出现由于竞争状态而致使的未定义标示符错误。有几个技术能够帮助咱们保证执行顺序。html

  • 硬编码回调 (Hardcoded Callback)
  • Window Onlad
  • 定时器 (Timer)
  • Degrading Script Tags

方法1:硬编码回调 (Hardcoded Callback)

让外部的脚本调用内部脚本的函数,以确保代码的顺序执行。例如linkjava

//行内代码
function init() {
  createMenu('examples');
}
var domscript = document.createElement('script');
domscript.src = "menu-with-init.js";
document.getElementsByTagName('head')[0].appendChild(domscript);

//外部文件
function createMenu(id) {
  [...]
}
// callback to the main page
init();

若是开发人员能够同时控制主页面和外部脚本,这种技术是可行的的。可是咱们经常会调用第三方的 JavaScript ,好比: jQuery ,咱们不可能降回调添加在 jQuery 的文件中。并且这种方法也不太灵活,一旦改变了回调函数须要同时修改外部脚本。git

方法2: Window Onload

经过监听 Window 的 onload 事件来触发行内代码的执行。这使得只要确保外部脚本在 window.onload 以前下载执行就能保证执行顺序。有些异步加载技术确保在 window.onload 触发以前加载外部脚步:github

  • Script in Iframe 在IE、Firefox、Safari、Chrome 和 Opera 中保持顺序执行浏览器

  • Script DOM 在Firefox、Safari 和 Chrome 中保持顺序执行app

使用其中一种技术,再经过 window.onload 触发行内脚本就能够实现并行下载的同时保证执行顺序。查看官网demo。这个例子使用了 Script in Iframe 方法加载外部脚本,几乎在全部的浏览器中它都会阻塞onload事件。外部脚本被嵌入在 menu.php 中,而后用 iframe 加载它而不是直接加载 menu.js 。依据浏览器的差别选用 addEventListener 或者 attachEvent 比简单的地使用 window.onload() 好一些。关于window.onload加载的多种解决方案 »dom

Window Onload 整合技术有两个缺点:首先,必须肯定异步脚本是经过阻塞 onload 事件的方式加载的。其次,可能会形成行内代码的延迟执行。若是页面还有不少其余的资源,好比图片等,那么外部脚本加载执行结束以后, window.onload内部的代码必须等到页面彻底加载以后才可以执行。一般行内脚本最好在外部脚本下载和执行以后当即调用。

方法3:定时器(Timer)

定时器技术指的是使用轮询方法来保证在行内代码执行之,前所依赖的外部脚本已经加载。《高性能网站建设进阶优化》一书给出的demo中能够看到link。修改行内代码,添加一个新函数 initTimer ,负责检查依赖的命名空间和标示符是否存在。若是存在,则调用须要调用的函数;若是不存在,就在指定的时间段以后再次调用 initTimer 函数检查命名空间和标示符。

function initTimer() {
    if ( "undefined" === typeof(EFWS) ) {
        setTimeout(initTimer, 300);
    }
    else {
        init();
    }
}

这个技术也有它的缺点。若是setTimeout方法中设置的事件间隔过小,可能会增长页面的开销。相反,若是设置太大,又可能形成外部脚本加载完成和行内代码开始执行之间的延迟。就上面的例子来讲,若是外部脚本加载失败,即行内脚本永远没法检测到指定的命名空间,轮询将会无限进行下去。同时稍微增长了维护的成本,若是外部文件的命名空间和标示符变了,行内代码也要更新。

方法4:Script Onload

前面的那些整合技术会增长页面的脆弱性,开销,致使页面的延迟。Script Onload 方法经过监听脚本的 onload 事件解决了全部的这些问题。link。考虑到浏览器之间的差别,添加了 script 元素的 onload 和 onreadystatechange 事件处理程序。onload 在其余浏览器中有效,Opera 二者都有效。

var DOMScript=document.createElement("script");
DOMScript.src="someting.js";
DOMScript.onloadDone=false;
DOMScript.onload=function(){
    DOMScript.onloadDone=true;
    init();
}
DOMScript.onreadystatechange=function(){
    if(("loaded" === DOMScript.readyState || "complete" === DOMScript.readyState) && ! DOMScript.onloadDone){
        DOMScript.onloadDone=true;
        init();
    }
}

Script Onload 是整合异步加载外部脚本和行内脚本的首选。不引用任何外部的标示符,因此维护简单。行内脚本能够在外部脚本加载以后当即执行。同时事件处理也很简单

……未完待续……

相关文章
相关标签/搜索