JavaScript性能优化之加载与执行

JavaScript在浏览区中的性能,能够认为是开发者所面临的最严重的可用性问题。 优化这个问题的第一步从它的加载和执行开始。javascript


霸道的script标签
script标签每次出现都会霸道地让页面等待脚本的解析和执行,不管当前的JavaScript代码是内嵌仍是外联,页面的下载和渲染都必须停下来等待脚本执行完毕。这是页面生存周期的必要环节,由于脚本执行过程当中可能会修改页面的内容。比较典型的是document.write()与innerHTML,在JavaScript脚本解析并执行这个过程当中,页面的渲染和用户交互是彻底阻塞的。java


脚本位置
通常状况下,我是习惯用外联方式引入js文件,而且一般将他们放在head标签上,可是如今我必需要改变这种习惯了。
这种看似正常的代码组织实际上有很是严重的性能问题:加入我在head标签内加载了两个JavaScript文件,那个人页面的渲染将等到这两个文件加载和执行完毕才开始,由于浏览器在解析body标签以前,不会渲染任何东西。在它们的加载过程当中,将会致使明显的延迟,一般的表现形式为:空白页面,内容没法浏览,没法交互。
之前的浏览器加载脚本是一个加载而且执行完成以后才会去加载下一个,如今这个问题有了改善,即如今容许并行下载JavaScript脚本了,可是上述问题仍然成立。因此这里我学习一下雅虎特别性能小组提出的优化JavaScript的首要原则:将脚本放在底部!浏览器


组织脚本
1001 > 254
由于每一个script标签初始下载都会阻塞页面渲染,因此咱们须要减小script标签的数量来限制HTTP请求数从而改善性能,最好将多个JavaScript文件合并成一个。缓存


无阻塞脚本
无阻塞脚本的秘诀在于:在页面加载完以后才加载JavaScript代码。这意味着在window对象的load事件触发后再下载脚本。这样作的好处是当你下载一个较大的JS文件时,浏览器仍是会锁死一段时间,为了不这种状况,咱们须要向页面逐步加载JavaScript文件。安全

  • 延迟的脚本app

script定义了一个扩展属性:defer。defer属性指明本元素所含的脚本不会修改DOM,所以代码可以安全地执行,可是浏览器的支持状况不理想。
除此以外,HTML5引入了async属性,用于异步加载脚本,asnyc与defer相同点在于都用于异步加载脚本,采用的是并行下载,在下载过程当中不会产生阻塞。区别在于,asnyc是在加载完成后自动执行,而defer须要等待加载页面完成后执行,在onload事件处理器执行以前被调用。异步

  • 动态脚本元素async

用于DOM的存在,咱们能够用JavaScript动态建立HTML里面的元素,固然也包括script元素,这是动态脚本元素的大前提。
这项技术的重点在于:我经过在页面内动态建立一个script元素,而且经过给其src属性赋值来加载脚本,那么脚本会在页面内script元素被建立后才开始加载,那么不管在什么时候启动下载,文件的下载和执行过程不会阻塞页面的其余进程。函数

  • 新建立的script元素放在哪?
    一般来说,把新建立的script标签添加到head比添加到body中更加保险,由于当body中的内容没有所有加载完成时,IE可能会抛出一个“操做已终止”的错误信息。性能

  • 动态建立的文件在被下载后怎样执行?
    对于Firefox,Opera,Chrome,Safari来讲,会在<script>元素接收完成时触发一个load事件;可是IE不一样,它触发readystatechange事件。<script>元素提供一个readyState属性,它的值在外链文件的下载过程当中的不一样阶段会发生不一样的变化,IE这些状态有些不会所有用到,显示很混乱,最有用的就是"loaded"和"complete"两种状态;

    • "uninitialized"--初始状态

    • "loading"--开始下载

    • "loaded"--下载完成

    • "interactive"--数据完成下载但尚不可用

    • "complete"--全部数据已经准备就绪

  • 动态脚本加载凭借它在跨浏览器兼容性和易用的优点,成为最通用无阻塞加载解决方案;

function loadScript(url,callback){
   var script = document.createElement("script");
   script.type = "text/javascript";
   //IE
   if(script.readyState){
   script.onreadystatechange = function(){
   if(script.readyState == "loaded"||script.readyState == "complete"){
   script.onreadystatechange == null;
   callback();//回调
                        }
                }
   };
   //其余浏览器
   script.onload = function(){
   callback();
   };  
   script.src = "file.js";//可用URL取代
   //HTML5中用document.head就行了
   document.getElementByTagName("head")[0].appendChild(script); 
   }

XHMHttpRequest脚本注入
这里是另外一种无阻塞脚本模式,先建立一个XHR对象,而后用它下载JavaScript文件,最后经过动态建立的<script>元素将代码注入页面中;

var xhr = new XMLHttpRequest();
  xhr.open("open","file.js",true);
  xhr.onreadystatechange = funtion(){
  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);
                }
        }
  }

首先发送一个get请求获取file.js文件,事件处理函数onReadyStateChange检查readyState是否为4,同时校准HTTP状态码是否为有效(2XX表示有效响应,304意味着从缓存读取);

  • 优点:
    因为代码是在script标签以外返回的,所以脚本下载完成后不会自动执行,这使得你能够把脚本的执行推迟到你准备好了以后进行;

各个浏览器都支持;

  • 缺点:
    JavaScript文件必须与所请求的页面处于相同的域,这意味着JavaScript文件不能从CDN下载;


总结
向页面中添加大量javascript的推荐作法只须要两步:
先添加动态加载所须要的代码,这段代码尽可能精简,甚至能够只包含loadScript()函数,
第二步就是经过调用函数来加载剩余的javascript。


对这种推荐作法也分为两种方式:
第一种是采用外联方式加载第一个javascript,并放在</body>以前,这样作的好处:确保javascript执行过程当中不会阻碍页面其余内容的显示,其次,当剩下的javascript文件完成下载时,应用所需的全部DOM结构已经建立完毕,并作好了交互准备,从而避免了须要另外一个事件,好比window.onload来检测页面是否准备好!
第二种方式很简单,就是将第一个javascript直接内嵌在页面,从而避免了多产生一次HTTP请求!


若是将JavaScript比做大海,那么要享受它,首先得确保它到达的是沙滩而不是悬崖,这体如今代码存放的位置,其次得让script大海若要波澜壮阔,则须要纳百川,大海的美一部分在于波浪,无阻塞模式的意义正是于此,经过有节奏地加载脚本提升JavaScript性能!

相关文章
相关标签/搜索