JavaScript 在浏览器中的性能,可以认为是开发者所面临得最严重的可用性问题。这个问题因JavaScript的阻塞特性变得复杂,

也就是说当浏览器在执行JavaScript代码时,不能同时做其他任何事情

多数浏览器使用单一进程来处理用户界面(UI)的刷新和JavaScript脚本执行,所以同一时刻只能做一件事。

JavaScript执行过程耗时越久,浏览器等待响应的时间就越长。

每次<script>标签出现都霸道的让页面等待脚本的解析和执行。

脚本位置

  将脚本放在底部:推荐将所有的<script>标签尽可能的放到<body>标签的底部,以尽量减少对整个页面下载的影响

  

脚本数量

  限制内嵌脚本的数量:由于每个<script>标签初始下载时都会阻塞页面渲染,所以减少页面包含的<script>标签数量有助于改善这一情况。这不仅仅针对外链脚本,内嵌脚本的数量也同样需要显示。浏览器在解析HTML页面的过程中没遇到一个<script>标签,都会因执行脚本而导致一定的延时。因此最小化延迟时间将会明显改善页面的总体性能。

  同时Steve Souder还发现,把一段内嵌脚本放在引用外链样式表的<link>标签之后会导致页面阻塞去等待样式表的下载。因此永远不要把内嵌脚本紧跟在<link>标签后面

  另外,下载一个100KB的文件将比下载4个25KB的文件更快。也就是说,减少页面中外链脚本文件的数量将会改善性能。可以把多个文件合并成一个,这样只需要引用一个<script>标签,减少性能消耗。

 无阻塞的脚本

  减少JavaScript文件大小和限制HTTP请求数有时候远远不够,WEB功能越丰富,所需要的JavaScript代码就越多,所以精简源代码并不总是可行,尽管下载单个较大的JavaScript文件只产生了一次HTTP请求,却会锁死浏览器一大段时间。这种情况下需要向页面上逐步的加载JavaScript文件,这样做在某种程度上来说不会阻塞浏览器。

  无阻塞脚本:在页面加载完成后才加载JavaScript代码。意味着在window对象的load事件触发后再下载脚本【defer和async】

 延迟的脚本

HTML4为<script>标签定义了扩展属性:defer。Defer属性指明本元素所含的脚本不会修改DOM,因此代码能安全的执行。该属性只有IE4和Firefox3.5+的浏览器支持,所以它不是一个理想的跨浏览器解决方案。其他浏览器中defer会被直接忽略。

defer受限:低版本IE浏览器不支持,如今所有的主流浏览器都已经实现对defer属性的支持,W3C规定:defer属性仅当src属性声明时才生效。

使用方法:

  1. <script type = "text/javascript" src = "file1.js" defer></script>
    或者
    <body>
      <script defer>
        alert("defer");
      </script>
  1.   <script>
        alert("script");
      </script>
  1.   <script>
        window.onload = function(){
         alert("load");
        }
  1.   </script>
    </body>
    //在支持defer的浏览器中,弹出的顺序是“script”,“defer”,“load”。注意:带有defer属性<script>元素不是跟在第二个后边执行,而是在onload事件处理器执行之前被调用

  async:HTML5中引入的async属性,用于异步加载脚本。async与defer都是采用并行下载,在下载过程中不会产生阻塞。

      区别在于执行时机,async是加载完成后自动执行,而defer是需要等待页面完成后执行。

动态脚本元素

由于文档对象模型DOM的存在,可以用JavaScript动态创建HTML中的几乎所有内容。

<script>的元素下载过程中,readyState属性有五种取值,其中“loaded”和“complete”是最常用的状态。

IE在标识最终状态的readyState的值时并不一致,有时候达到loaded状态 ,有时候达到complete状态。所以使用readystatechange事件的最靠谱方式就是同时检查这两种状态,只要触发其中一个就删除事件处理器。(以确保事件不会被处理两次)

下边的函数封装了标准及IE特有的实现方法:

  1. function loadScript(url,callback){
  2. var script = document.creatElement("script");
  3. script.type = "text/script";
  4.  
  5. if(script.readyState){ //IE
  6.   script.onreadystatechange = function(){
          if(script.readyState == "loaded" || script.readyState == "complete"){
            script.onreadystatechange = null;
            callback();
          }
        }
  7. }else{  //其他浏览器
         script.onload = function(){
            callback();
         }
       }
      script.src = url;
      document.getElementsByTagName("head")[0].appendChild(script);
  8. }

如果需要你可以动态加载尽可能多的javaScript文件到页面上,但是一定要考虑文件的加载顺序。在所有主流浏览器中,只有Firefox和Opera能保证脚本会按照你指定的顺序执行,其他浏览器将会按照从服务端返回的顺序下载和执行代码,你可以将下载操作串联起来以保证下载顺序:

  1. loadScript("file1",function(){
  2. loadScript("file2",function(){
  3. loadScript("file3",function(){
  4. alert("All files are loaded!!");
  5.     })
  6.   })
  7. })

弊端:如果需要下载很多的文件,这个方案就会带来一点管理上的麻烦。

  如果多个文件的下载顺序很重要,最好的做法就是把他们按正确的顺序合并成一个文件。下载这个文件就能一次获得所有的代码(这个过程是异步的,文件大一点也不会有影响)

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

 XMLHttpResquest脚本注入

另一种无阻塞加载脚本的方法是使用XMLHttpResquest(XHR)对象获取脚本并注入页面中。

  1. var xhr = new XMLHttpRequest();
  2. xhr.open("get","file1.js",true);
  3. xhr.onreadystatechange = function(){
  4. if(xhr.readyState == ){
  5. if(xhr.status >= && xhr.status < || xhr.status == ){
  6. var script = document.createElement("script");
  7. script.type = "text/javascript";
  8. script.text = xhr.reaponseText;
  9. document.body.appendChild(script);
  10. }
  11. }
  12. }
    xhr.send(null);

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

然后创建<script>元素,并设置属性。一旦新创建的元素被添加到页面,代码会立刻执行然后准备就绪。

优点:你可以下载JavaScript代码但不立即执行,由于代码是在<script>标签之外返回的,因此它下载之后不会自动执行,这使得你可以把脚本的执行推迟到你准备好的时候,

   同样的代码在所有主流浏览器中无一例外都能正常工作。

局限性:JavaScript文件必须与所请求的页面处于相同的域,这意味着JavaScript文件不能从CDN上下载,因此大型Web应用不会采用XHR脚本注入技术

推荐的无阻塞模式

向页面添加大量JavaScript的推荐做法只需两步:先添加动态加载所需的代码,然后加载初始化页面所需的剩下的代码。因为第一部分的代码尽量精简,甚至可能只包含loadScript()函数,它下载执行都很快,所以不会对页面有太多影响。一旦初始代码就位,就用它来加载剩余的JavaScript。

  1. <script type="text/javascript" src = "loader.js">
  2. <script type="text/javascript">
  3. loadScript("the-rest.js",function(){
  4. Application.init();
  5. });
  6. </script>

把这段代码放在</body>闭合标签之前。

好处:确保了JavaScript执行过程中不会阻碍页面其他内容的显示。其次,当第二个JavaScript文件完成下载时,应用所需的所有DOM结构已经创建完毕,并做好了交互准备,从而避免了需要另一个事件(比如window.onload)来检测页面是否准备好。

当然可以把loadScript()函数直接嵌入页面,省去了加载<script type="text/javascript" src = "loader.js">时的一次http请求。

 YUI3的方法:没看明白...

 LazyLoad类库

雅虎的工程师创建了一个更为通用的延迟加载工具:LazyLoad(源代码:http://github.com/rgrove/lazyload/)。LazyLoad是loadScript()函数的增强版。

  1. <script type="text/javascript" src = "lazyload.min.js">
  2. <script type="text/javascript">
  3. LazyLoad.js("the-rest.js",function(){
  4. Application.init();
  5. });
  6. </script>
  7.  
  8. //LazyLoad支持下载多个JavaScript文件,需要传入一个URL数组
  9.  
  10. <script type="text/javascript" src = "lazyload.min.js">
  11. <script type="text/javascript">
  12. LazyLoad.js(["the-rest.js","last-file.js"],function(){
  13. Application.init();
  14. });
  15. </script>

分析:尽管这种无阻塞的方式可以动态加载很多文件,但是建议尽量减少文件数。因为每个文件的下载都是一个独立的http请求,而且回调函数会等待所有文件都下载完成后才会执行

LABjs

LABjs(http://labjs.com)可以进行链式调用

  1. <script type="text/javascript" src = "lab.js">
  2. <script type="text/javascript">
  3. $LAB.script("first-file.js")
         .script("the-rest.js")
         .wait(function(){
            Application.init();
        });
    </script>

为了保证first-file.js在the-rest.js的代码前执行,需要再第一个script()方法后调用wait():

  1. <script type="text/javascript" src = "lab.js">
  2. <script type="text/javascript">
  3. $LAB.script("first-file.js").wait()
  4.     .script("the-rest.js")
  5.     .wait(function(){
  6.       Application.init();
  7.     });
  8. </script>

尽管是并行下载,但是first-file.js中的代码肯定能在the-rest.js前面执行。

小结:

1、</body>闭合标签之前,将所有的<script>标签放在页面底部。能确保在脚本执行前页面已经完成了渲染

2、合并脚本,页面中的<script>标签越少,加载也就越快,响应也就越迅速。无论外链文件还是内嵌脚本都是如此。

3、有多种无阻塞下载JavaScript的方法

  使用<script>标签的defer属性

  使用动态创建的<script>元素来下载并执行代码

  使用XHR对象下载JavaScript代码并注入页面中

通过以上策略,可以极大的提高那些需要使用大量JavaScript的Web应用的实际性能

JS性能优化——加载和执行的更多相关文章

  1. 对于HTML页面中CSS, JS, HTML的加载与执行过程的简单分析

    来自   https://blog.csdn.net/u011088260/article/details/79563315   最近在研究HTML页面中JavaScript的执行顺序问题.在Java ...

  2. js&jquery页面加载完执行

    js <script type=”text/javascript”> window.onload=function (){ var userName=”xiaoming”; alert(u ...

  3. js dom元素加载完成执行

    //dom ready执行 function ready(fn){ if(document.addEventListener){ document.addEventListener('DOMConte ...

  4. JS脚本加载与执行对性能的影响

    高性能JavaScript-JS脚本加载与执行对性能的影响 在web产品优化准则中,很重要的一条是针对js脚本的加载和执行方式的优化.本篇文章简单描述一下其中的优化准则. 1. 脚本加载优化 1.1 ...

  5. 前端性能优化:细说JavaScript的加载与执行

    本文主要是从性能优化的角度来探讨JavaScript在加载与执行过程中的优化思路与实践方法,既是细说,文中在涉及原理性的地方,不免会多说几句,还望各位读者保持耐心,仔细理解,请相信,您的耐心付出一定会 ...

  6. 高性能JavaScript-JS脚本加载与执行对性能的影响

    在web产品优化准则中,很重要的一条是针对js脚本的加载和执行方式的优化.本篇文章简单描述一下其中的优化准则. 1. 脚本加载优化 1.1 脚本位置对性能的影响 优化页面加载性能的原则之一是将scri ...

  7. 【转】js JavaScript 的性能优化:加载和执行

    JavaScript 的性能优化:加载和执行 转自:https://www.ibm.com/developerworks/cn/web/1308_caiys_jsload/ 随着 Web2.0 技术的 ...

  8. 性能优化-css,js的加载与执行

    前端性能优化 css,js的加载与执行 javascript是单线程的 一个网站在浏览器是如何进行渲染的呢? html页面加载渲染的过程 html渲染过程的一些特点 顺序执行,并发加载 词法分析 并发 ...

  9. 前端性能优化 css和js的加载与执行

    一个网站在浏览器端是如何进行渲染的? html本身首先会被渲染成 DOM 树,实际上 html 是最先通过网址请求过来的,请求过来之后,html 本身会由一个字节流转化成一个字符流,浏览器端拿的就是字 ...

随机推荐

  1. Linux基础学习5

    程序管理与SELinux初探 process&program  程序 (program):通常为 binary program ,放置在储存媒体中 (如硬盘.光盘.软盘.磁带等), 为实体档案 ...

  2. Manifest值冲突解决方法

    FBI Warning:欢迎转载,但请标明出处:http://blog.csdn.net/codezjx/article/details/38669939,未经本人同意请勿用于商业用途,感谢支持! 整 ...

  3. 在网页里插入flash的代码

    <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://down ...

  4. svm中 C 和sigma对街宽和分隔平面的影响

    C越大 街越窄,可能引发过拟合,对于噪声的惩罚力度加大. sigma越小,高斯分布长得又高又瘦, 会造成只会作用于支持向量样本附近,对于未知样本分类效果很差,存在训练准确率可以很高,

  5. CHM Navigation to the webpage was canceled 解决办法

    在网上下载了一些chm文件,打开后看不到内容,显示:Navigation to the webpage was canceled 如图:             解决方法如下: 在文件上右键,选择“属 ...

  6. MFC中的CDC详细教程

    参考:  MFC中的CDC详细教程1,2,3 StretchDIBits用法

  7. leetcode笔记:Ugly Number II

    一. 题目描写叙述 Write a program to find the n-th ugly number. Ugly numbers are positive numbers whose prim ...

  8. mysql启动參数(/etc/my.cnf)具体解释汇总

    在linux以下的/etc/my.cnf的參数具体解释汇总 MYSQL–my.cnf配置中文具体解释 basedir = path   使用给定文件夹作为根文件夹(安装文件夹). character- ...

  9. sql字符串处理

    sql字符串截取 substring(ParamName,start,length) start开始位置,length结束位置 DECLARE @Tel NVARCHAR(20) SET @Tel=' ...

  10. Jest — ElasticSearch Java 客户端

    1. 介绍 任何使用过Elasticsearch的人都知道,使用基于rest的搜索API构建查询可能是单调乏味且容易出错的. 在本教程中,我们将研究Jest,一个用于Elasticsearch的HTT ...