GVKun编程网logo

JavaScript 的性能优化:加载和执行(以及动态引入的外部 JS 文件在各浏览器中的加载顺序不一致)

9

如果您对JavaScript的性能优化:加载和执行和以及动态引入的外部JS文件在各浏览器中的加载顺序不一致感兴趣,那么这篇文章一定是您不可错过的。我们将详细讲解JavaScript的性能优化:加载和执

如果您对JavaScript 的性能优化:加载和执行以及动态引入的外部 JS 文件在各浏览器中的加载顺序不一致感兴趣,那么这篇文章一定是您不可错过的。我们将详细讲解JavaScript 的性能优化:加载和执行的各种细节,并对以及动态引入的外部 JS 文件在各浏览器中的加载顺序不一致进行深入的分析,此外还有关于javacript加载和执行、javascript – 触发window.onload事件时加载了哪些外部资源以及资源的加载顺序是什么?、Javascript 加载和执行-性能提高篇_javascript技巧、JavaScript 在各个浏览器中执行的耐性_javascript技巧的实用技巧。

本文目录一览:

JavaScript 的性能优化:加载和执行(以及动态引入的外部 JS 文件在各浏览器中的加载顺序不一致)

JavaScript 的性能优化:加载和执行(以及动态引入的外部 JS 文件在各浏览器中的加载顺序不一致)

一、JavaScript 的性能优化:加载和执行

随着 Web2.0 技术的不断推广,越来越多的应用使用 JavaScript 技术在客户端进行处理,从而使 JavaScript 在浏览器中的性能成为开发者所面临的最重要的可用性问题。而这个问题又因 JavaScript 的阻塞特性变的复杂,也就是说当浏览器在执行 JavaScript 代码时,不能同时做其他任何事情。本文详细介绍了如何正确的加载和执行 JavaScript 代码,从而提高其在浏览器中的性能。

概览

无论当前 JavaScript 代码是内嵌还是在外链文件中,页面的下载和渲染都必须停下来等待脚本执行完成。JavaScript 执行过程耗时越久,浏览器等待响应用户输入的时间就越长。浏览器在下载和执行脚本时出现阻塞的原因在于,脚本可能会改变页面或 JavaScript 的命名空间,它们对后面页面内容造成影响。一个典型的例子就是在页面中使用document.write()。例如清单 1

清单 1 JavaScript 代码内嵌示例

<html>
<head>
    <title>Source Example</title>
</head>
<body>
    <p>
    <script type="text/javascript">
        document.write("Today is " + (new Date()).toDateString());
    </script>
    </p>
</body>
</html>

当浏览器遇到<script>标签时,当前 HTML 页面无从获知 JavaScript 是否会向<p> 标签添加内容,或引入其他元素,或甚至移除该标签。因此,这时浏览器会停止处理页面,先执行 JavaScript代码,然后再继续解析和渲染页面。同样的情况也发生在使用 src 属性加载 JavaScript的过程中,浏览器必须先花时间下载外链文件中的代码,然后解析并执行它。在这个过程中,页面渲染和用户交互完全被阻塞了。

脚本位置

HTML 4 规范指出 <script> 标签可以放在 HTML 文档的<head><body>中,并允许出现多次。Web 开发人员一般习惯在 <head> 中加载外链的 JavaScript,接着用 <link> 标签用来加载外链的 CSS 文件或者其他页面信息。例如清单 2

清单 2 低效率脚本位置示例

<html>
<head>
    <title>Source Example</title>
    <script type="text/javascript" src="script1.js"></script>
    <script type="text/javascript" src="script2.js"></script>
    <script type="text/javascript" src="script3.js"></script>
    <link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
    <p>Hello world!</p>
</body>
</html>

然而这种常规的做法却隐藏着严重的性能问题。在清单 2 的示例中,当浏览器解析到 <script> 标签(第 4 行)时,浏览器会停止解析其后的内容,而优先下载脚本文件,并执行其中的代码,这意味着,其后的 styles.css 样式文件和<body>标签都无法被加载,由于<body>标签无法被加载,那么页面自然就无法渲染了。因此在该 JavaScript 代码完全执行完之前,页面都是一片空白。图 1 描述了页面加载过程中脚本和样式文件的下载过程。

图 1 JavaScript 文件的加载和执行阻塞其他文件的下载

我们可以发现一个有趣的现象:第一个 JavaScript 文件开始下载,与此同时阻塞了页面其他文件的下载。此外,从 script1.js 下载完成到 script2.js 开始下载前存在一个延时,这段时间正好是 script1.js 文件的执行过程。每个文件必须等到前一个文件下载并执行完成才会开始下载。在这些文件逐个下载过程中,用户看到的是一片空白的页面。

从 IE 8、Firefox 3.5、Safari 4 和 Chrome 2 开始都允许并行下载 JavaScript 文件。这是个好消息,因为<script>标签在下载外部资源时不会阻塞其他<script>标签。遗憾的是,JavaScript 下载过程仍然会阻塞其他资源的下载,比如样式文件和图片。尽管脚本的下载过程不会互相影响,但页面仍然必须等待所有 JavaScript 代码下载并执行完成才能继续。因此,尽管最新的浏览器通过允许并行下载提高了性能,但问题尚未完全解决,脚本阻塞仍然是一个问题。

由于脚本会阻塞页面其他资源的下载,因此推荐将所有<script>标签尽可能放到<body>标签的底部,以尽量减少对整个页面下载的影响。例如清单 3

清单 3 推荐的代码放置位置示例

<html>
<head>
    <title>Source Example</title>
    <link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
    <p>Hello world!</p>

    <!-- Example of efficient script positioning -->
    <script type="text/javascript" src="script1.js"></script>
    <script type="text/javascript" src="script2.js"></script>
    <script type="text/javascript" src="script3.js"></script>
</body>
</html>

这段代码展示了在 HTML 文档中放置<script>标签的推荐位置。尽管脚本下载会阻塞另一个脚本,但是页面的大部分内容都已经下载完成并显示给了用户,因此页面下载不会显得太慢。这是优化 JavaScript 的首要规则:将脚本放在底部。

组织脚本

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

这个问题在处理外链 JavaScript 文件时略有不同。考虑到 HTTP 请求会带来额外的性能开销,因此下载单个 100Kb 的文件将比下载 5 个 20Kb 的文件更快。也就是说,减少页面中外链脚本的数量将会改善性能。

通常一个大型网站或应用需要依赖数个 JavaScript 文件。您可以把多个文件合并成一个,这样只需要引用一个<script>标签,就可以减少性能消耗。文件合并的工作可通过离线的打包工具或者一些实时的在线服务来实现。

需要特别提醒的是,把一段内嵌脚本放在引用外链样式表的<link>之后会导致页面阻塞去等待样式表的下载。这样做是为了确保内嵌脚本在执行时能获得最精确的样式信息。因此,建议不要把内嵌脚本紧跟在<link>标签后面。

无阻塞的脚本

减少 JavaScript 文件大小并限制 HTTP 请求数在功能丰富的 Web 应用或大型网站上并不总是可行。Web 应用的功能越丰富,所需要的 JavaScript 代码就越多,尽管下载单个较大的 JavaScript 文件只产生一次 HTTP 请求,却会锁死浏览器的一大段时间。为避免这种情况,需要通过一些特定的技术向页面中逐步加载 JavaScript 文件,这样做在某种程度上来说不会阻塞浏览器。

无阻塞脚本的秘诀在于,在页面加载完成后才加载 JavaScript 代码。这就意味着在 window 对象的 onload事件触发后再下载脚本。有多种方式可以实现这一效果。

延迟加载脚本

HTML 4 为<script>标签定义了一个扩展属性:deferDefer 属性指明本元素所含的脚本不会修改 DOM,因此代码能安全地延迟执行。defer属性只被 IE 4 和 Firefox 3.5 更高版本的浏览器所支持,所以它不是一个理想的跨浏览器解决方案。在其他浏览器中,defer 属性会被直接忽略,因此<script>标签会以默认的方式处理,也就是说会造成阻塞。然而,如果您的目标浏览器支持的话,这仍然是个有用的解决方案。清单 4 是一个例子

清单 4 defer 属性使用方法示例

<script type="text/javascript" src="script1.js" defer></script>

带有 defer 属性的<script>标签可以放置在文档的任何位置。对应的 JavaScript 文件将在页面解析到<script>标签时开始下载,但不会执行,直到 DOM 加载完成,即onload事件触发前才会被执行。当一个带有 defer 属性的 JavaScript 文件下载时,它不会阻塞浏览器的其他进程,因此这类文件可以与其他资源文件一起并行下载。

任何带有 defer 属性的<script>元素在 DOM 完成加载之前都不会被执行,无论内嵌或者是外链脚本都是如此。清单 5 的例子展示了defer属性如何影响脚本行为:

清单 5 defer 属性对脚本行为的影响

<html>
<head>
    <title>Script Defer Example</title>
</head>
<body>
    <script type="text/javascript" defer>
        alert("defer");
    </script>
    <script type="text/javascript">
        alert("script");
    </script>
    <script type="text/javascript">
        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 脚本确实有用。如果您需要支持跨领域的多种浏览器,那么还有更一致的实现方式。

HTML 5 为<script>标签定义了一个新的扩展属性:async。它的作用和 defer 一样,能够异步地加载和执行脚本,不因为加载脚本而阻塞页面的加载。但是有一点需要注意,在有 async 的情况下,JavaScript 脚本一旦下载好了就会执行,所以很有可能不是按照原本的顺序来执行的。如果 JavaScript 脚本前后有依赖性,使用 async 就很有可能出现错误。

动态脚本元素

文档对象模型(DOM)允许您使用 JavaScript 动态创建 HTML 的几乎全部文档内容。<script>元素与页面其他元素一样,可以非常容易地通过标准 DOM 函数创建:

清单 6 通过标准 DOM 函数创建<script>元素

var script = document.createElement ("script");
    script.type = "text/javascript";
    script.src = "script1.js";
    document.getElementsByTagName("head")[0].appendChild(script);

新的<script>元素加载 script1.js 源文件。此文件当元素添加到页面之后立刻开始下载。此技术的重点在于:无论在何处启动下载,文件的下载和运行都不会阻塞其他页面处理过程。您甚至可以将这些代码放在<head>部分而不会对其余部分的页面代码造成影响(除了用于下载文件的 HTTP 连接)。

当文件使用动态脚本节点下载时,返回的代码通常立即执行(除了 Firefox 和 Opera,他们将等待此前的所有动态脚本节点执行完毕)。当脚本是“自运行”类型时,这一机制运行正常,但是如果脚本只包含供页面其他脚本调用调用的接口,则会带来问题。这种情况下,您需要跟踪脚本下载完成并是否准备妥善。可以使用动态 <script> 节点发出事件得到相关信息。

Firefox、Opera, Chorme 和 Safari 3+会在<script>节点接收完成之后发出一个 onload 事件。您可以监听这一事件,以得到脚本准备好的通知:

清单 7 通过监听 onload 事件加载 JavaScript 脚本

var script = document.createElement ("script")
script.type = "text/javascript";

//Firefox, Opera, Chrome, Safari 3+
script.onload = function(){
    alert("Script loaded!");
};

script.src = "script1.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 事件句柄(保证事件不会被处理两次):

清单 8 通过检查 readyState 状态加载 JavaScript 脚本

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 = "script1.js";
document.getElementsByTagName("head")[0].appendChild(script);

大多数情况下,您希望调用一个函数就可以实现 JavaScript 文件的动态加载。下面的函数封装了标准实现和 IE 实现所需的功能:

清单 9 通过函数进行封装

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() 函数使用方法如下:

清单 10 loadScript()函数使用方法

loadScript("script1.js", function(){
    alert("File is loaded!");
});

您可以在页面中动态加载很多 JavaScript 文件,但要注意,浏览器不保证文件加载的顺序。所有主流浏览器之中,只有 Firefox 和 Opera 保证脚本按照您指定的顺序执行。其他浏览器将按照服务器返回它们的次序下载并运行不同的代码文件。您可以将下载操作串联在一起以保证他们的次序,如下:

清单 11 通过 loadScript()函数加载多个 JavaScript 脚本

loadScript("script1.js", function(){
    loadScript("script2.js", function(){
        loadScript("script3.js", function(){
            alert("All files are loaded!");
        });
    });
});

此代码等待 script1.js 可用之后才开始加载 script2.js,等 script2.js 可用之后才开始加载 script3.js。虽然此方法可行,但如果要下载和执行的文件很多,还是有些麻烦。如果多个文件的次序十分重要,更好的办法是将这些文件按照正确的次序连接成一个文件。独立文件可以一次性下载所有代码(由于这是异步进行的,使用一个大文件并没有什么损失)。

动态脚本加载是非阻塞 JavaScript 下载中最常用的模式,因为它可以跨浏览器,而且简单易用。

使用 XMLHttpRequest(XHR)对象

此技术首先创建一个 XHR 对象,然后下载 JavaScript 文件,接着用一个动态 <script> 元素将 JavaScript 代码注入页面。清单 12 是一个简单的例子:

清单 12 通过 XHR 对象加载 JavaScript 脚本

var xhr = new XMLHttpRequest();
xhr.open("get", "script1.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);

此代码向服务器发送一个获取 script1.js 文件的 GET 请求。onreadystatechange 事件处理函数检查 readyState 是不是 4,然后检查 HTTP 状态码是不是有效(2XX 表示有效的回应,304 表示一个缓存响应)。如果收到了一个有效的响应,那么就创建一个新的<script>元素,将它的文本属性设置为从服务器接收到的 responseText 字符串。这样做实际上会创建一个带有内联代码的<script>元素。一旦新<script>元素被添加到文档,代码将被执行,并准备使用。

这种方法的主要优点是,您可以下载不立即执行的 JavaScript 代码。由于代码返回在<script>标签之外(换句话说不受<script>标签约束),它下载后不会自动执行,这使得您可以推迟执行,直到一切都准备好了。另一个优点是,同样的代码在所有现代浏览器中都不会引发异常。

此方法最主要的限制是:JavaScript 文件必须与页面放置在同一个域内,不能从 CDN 下载(CDN 指"内容投递网络(Content Delivery Network)",所以大型网页通常不采用 XHR 脚本注入技术。

总结

减少 JavaScript 对性能的影响有以下几种方法:

  • 将所有的<script>标签放到页面底部,也就是</body>闭合标签之前,这能确保在脚本执行前页面已经完成了渲染。
  • 尽可能地合并脚本。页面中的<script>标签越少,加载也就越快,响应也越迅速。无论是外链脚本还是内嵌脚本都是如此。
  • 采用无阻塞下载 JavaScript 脚本的方法:
    • 使用<script>标签的 defer 属性(仅适用于 IE 和 Firefox 3.5 以上版本);
    • 使用动态创建的<script>元素来下载并执行代码;
    • 使用 XHR 对象下载 JavaScript 代码并注入页面中。

通过以上策略,可以在很大程度上提高那些需要使用大量 JavaScript 的 Web 网站和应用的实际性能。

二、BX9013: 动态引入的外部 JS 文件在各浏览器中的加载顺序不一致问题

问题描述

页面开发过程中,为了避免页面加载时引入过多外部 JS 文件,导致阻塞页面内容下载及渲染的情况出现。将会采用页面内容加载完成后,动态加载外部 JavaScript 文件的方法来解决此类问题。但是,需要注意的是,常用动态插入外部脚本文件的方法在各浏览器中的执行顺序并不一致。

造成的影响

对于动态插入的 SCRIPT 文件,不能保证在各浏览器能阻塞其后脚本的执行。

受影响的浏览器

所有浏览器  

问题分析

使用 appenChild insertBefore 等方法向文档中动态插入 SCRIPT 节点后,各浏览器中对脚本的执行顺序存在差异。

以下例子中均使用脚本代码插入远程文件:http://code.jquery.com /jquery-1.4.2.js ( jQuery 源码 ),该文件中定义了全局变量 $,当远程脚本文件加载完成后该变量将可用 。

情况1:

<script>
var js = document.createElement("script");
document.getElementsByTagName("head")[0].appendChild(js);
js.src = ''http://code.jquery.com/jquery-1.4.2.js'';
</script>

<script type="text/javascript">
alert($)
</script>

代码首先创建 SCRIPT 标记并插入到 HEAD 标记中,再将 SCRIPT 的 src 属性指向外部 JS 文件,由之后的 SCRIPT 标记中代码调用外部程序全局变量。

各浏览器表现及分析如下:

IE Firefox Opera 弹出($)函数体。此方法中动态附加进文档的 js 文件会阻断下一个 SCRIPT 标记内的代码解析,直至它全部解析完。
Chrome Safari 脚本出错,"$ is not defined"。此方法中动态附加进文档的 js 文件不会阻断下一个 SCRIPT 标记内的代码解析。

 

情况2:

<script type="text/javascript">
var js = document.createElement("script");
js.src = ''http://code.jquery.com/jquery-1.4.2.js'';
document.getElementsByTagName("head")[0].appendChild(js);
</script>

<script type="text/javascript">
alert($)
</script>

代码首先创建 SCRIPT 标记,将 src 属性指向外部 JS 文件。最后插入到 HEAD 标记中,由之后的 SCRIPT 标记中代码调用外部程序全局变量。

各浏览器表现及分析如下:

Firefox Opera 弹出($)函数体。此方法中动态附加进文档的 js 文件会阻断下一个 SCRIPT 标记内的代码解析,直至它全部解析完。
IE Chrome Safari 脚本出错,"$ is not defined"。此方法中动态附加进文档的 js 文件不会阻断下一个 SCRIPT 标记内的代码解析。

情况3:

<script type="text/javascript">
var js = document.createElement("script");
document.getElementsByTagName("head")[0].appendChild(js);
js.src = ''http://code.jquery.com/jquery-1.4.2.js'';
alert($)
</script>

代码首先创建 SCRIPT 标记,将 src 属性指向外部 JS 文件。最后插入到 HEAD 标记中,其后代码立即调用外部程序全局变量。

各浏览器表现及分析如下:

所有浏览器 脚本出错,"$ is not defined"。此方法中动态附加进文档的 js 文件不会阻断同一个 SCRIPT 标记内的代码解析。

情况4:

<script id="a"></script>

<script type="text/javascript">
var a=document.getElementById(''a'');
a.src = ''http://code.jquery.com/jquery-1.4.2.js'';
alert($)
</script>

代码获取某个 SCRIPT 标记的引用,在将其 src 属性指向外部 JS 文件,其后代码立即调用外部程序全局变量。

各浏览器表现及分析如下:

所有浏览器 脚本出错,"$ is not defined"。此方法中动态附加进文档的 js 文件不会阻断同一个 SCRIPT 标记内的代码解析。

情况5:

<script id="a" ></script>

<script type="text/javascript">
var a=document.getElementById(''a'');
a.src = ''http://code.jquery.com/jquery-1.4.2.js'';
</script>

<script type="text/javascript">
alert($)
</script>

代码获取某个 SCRIPT 标记的引用,在将其 src 属性值变更为外部 JS 文件,由之后的 SCRIPT 标记中代码调用外部程序全局变量。

各浏览器表现及分析如下:

IE Firefox Opera 弹出($)函数体。此方法中动态附加进文档的 js 文件会阻断下一个 SCRIPT 标记内的代码解析,直至它全部解析完。
Chrome Safari 脚本出错,"$ is not defined"。此方法中动态附加进文档的 js 文件不会阻断下一个 SCRIPT 标记内的代码解析。

 

综合以上情况,对于动态插入的 SCRIPT 文件,使用不同的插入方法将有不同的表现,不能保证在各浏览器能阻塞其后脚本的执行。

解决方案

对于必须动态附加到文档的外部 js 文件,要保证动态引入的脚本全部执行完成后,才能执行后续代码。

可以将此部分代码封装后调用,如:

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);

参考链接

http://www.w3help.org/zh-cn/causes/BX9013#

http://www.ibm.com/developerworks/cn/web/1308_caiys_jsload/

javacript加载和执行

javacript加载和执行

写在前面

JavaScript在浏览器中的性能,可认为是开发者所要面对的最重要的可用性的问题,此问题因JavaScript的阻塞特征而复杂,也就是说JavaScript运行时其他的事情不能被浏览器处理,事实上,大多数浏览器使用单进程处理UI更新和JavaScript运行等多个任务,而同一时间只能有一个任务被执行。JavaScript运行了多长时间,那么浏览器空闲下来响应用户输入之前的等待时间就有多长。

从基本层面说,这就意味着<script>标签的出现使整个页面因脚本解析、运行出现等待。不论实际的JavaScript代码是内联的还是包含在一个不相干的外部文件中页面下载和解析过程必须停下,等待脚本完成这些处理,然后才能继续,也是页面生命周期必不可少的部分,因为脚本可能在运行过程中修改页面内容。

在加载JavaScript过程中,页面解析和用户交互是被完全阻塞的。

脚本位置

 HTML 4 文档指出,一个<script>标签可以放在 HTML文档的 <head> 或者<body>标签中,可以在其中多次出现。传统上,<script> 标签用于加载外部JavaScript 文件。<head>部分除此类代码外,还包含 <link>标签用于加载外部css文件和其他页面中间件。也就是说,最好把风格和行为所依赖的部分放在一起,首先加载他们,使他们可以得到正确的外观和行为。

例如:

复制代码

 1 <html>  2 <head> 3 <title>Script Example</title> 4 <-- Example of ineffi cient script positioning --> 5 <script type="text/javascript" src="file1.js"></script> 6 <script type="text/javascript" src="file2.js"></script> 7 <script type="text/javascript" src="file3.js"></script> 8 <link rel="stylesheet" type="text/css" href="styles.css"> 9 </head>10 <body>11 <p>Hello world!</p>12 </body>13 </html>

复制代码


虽然这些代码看起来没什么问题,但是在〈head〉部分加载了三个JavaScript文件。每个〈script〉标签阻塞了页面解析过程,直到完整的下载并运行了外部JavaScript代码之后,页面才能继续进行。在浏览器没有遇到〈body〉标签之前,不会渲染页面的任何部分。

把脚本放在页面的顶端,将会导致一个可以察觉的延迟,通常表现为:页面打开一片白,用户不能阅读和操作。

如图,当第一javas文件开始下载时,阻塞了其他文件下载。进一步当第一个文件下载完成之后和第二个文件下载之前有一个延时,是第一个文件完全运行所需要的时间。

解决这个问题推荐的办法是:将所有<script> 标签放在尽可能接近<body> 标签的底部位置,尽量减少对整个页面下载的影响。

如:

复制代码

 1 <html> 2 <head> 3 <title>Script Example</title> 4 <link rel="stylesheet" type="text/css" href="styles.css"> 5 </head> 6 <body> 7 <p>Hello world!</p> 8 <-- Example of recommended script positioning --> 9 <script type="text/javascript" src="file1.js"></script>10 <script type="text/javascript" src="file2.js"></script>11 <script type="text/javascript" src="file3.js"></script>12 </body>13 </html>

复制代码

组成脚本

 由于每个<script>标签下载时阻塞页面解析过程,所以限制页面的<script>总数也是可以改善性能。这个规则对内联脚本和外部脚本同样适用。每当页面解析碰到一个<script>标签时,紧接着有一段时间用于代码执行。最小化这些延迟时间可以改善页面的整体性能。

每个HTTP请求都会产生额外的性能负担,下载一个100KB的文件比下载4个25KB的文件要快。总之,减少引用外部文件的数量。典型的,一个大型网站或者网页应用需要多次请求JavaScript文件。你可以将这些文件整合成一个文件,只需要一个<script>标签引用,就可以减少性能损失。

非阻塞脚本

 JavaScript倾向于阻塞浏览器某些处理过程,如HTTP请求和界面刷新,这是开发者面临的最显著的性能问题。保持JavaScript文件短小,并限制HTTP请求的数量,只是创建反应迅速的网页应用的第一步。一个应用程序所包含的功能越多,所需要的JavaScript代码就越大,保持源码短小并不总是一种选择。尽可能下载一个大JavaScript文件只产生一次HTTP请求。却会锁住浏览器一大段时间。为避开这种情况,你需要向页面中逐步添加JavaScript,某种程度上说不会阻塞浏览器。

非阻塞脚本的秘密在于,等页面加载之后,再加载JavaScript源码。从技术角度上讲,这意味着在window的load事件发出之后下载代码。有几种方法可以实现这种效果。

1.延期脚本

HTML4为<script>标签定义了一个扩展属性:defer。这个defer属性指明元素中所包含的脚本不打算修改DOM,因此代码可以稍后执行(适用于IE4以上浏览器)

<script type="text/javascript" src="file1.js" defer></script>

带有该属性的JavaScript文件在<script>被解析时启动下载,但代码不会被执行,直到DOM加载完成,它不会阻塞浏览器的其他处理过程,所以这些文件可以与页面的其他资源一起并行下载。
2.动态脚本元素

文档对象模型dom允许使用JavaScript动态创建HTML的几乎全部文档内容。其根本在于<script>元素与页面其他元素没有什么不同。
 当文件使用动态脚本节点下载时,返回的代码通常立即执行。当脚本“自运行”类型时这一机制运行正常,但是如果脚本只包含页面其他脚本调用的的接口,则会带来问题。这种情况下,你需要跟踪脚本下载完成并准备妥善的情况。
IE 会发出一个readystatechange事件。<script>元素有一个readyState属性,它的值随着外部下载的过程而改变。readyState有5种取值。
uninitialized       默认状态
loading             开始下载
interactive        下载完成但尚不可用
complete          所有数据都已经准备好

下面封装一个函数来实现JavaScript文件的动态加载:

复制代码

 1 function loadScript (url, callback){ 2 var script = document.createElement ("script") 3 script.type = "text/javascript"; 4 if (script.readyState){ //IE 5 script.onreadystatechange = function(){ 6 if (script.readyState == "loaded" ||  script.readyState == "complete"){ 7 script.onreadystatechange = null; 8 callback(); 9 }10 };11 } else {  //Others12 script.onload = function(){13 callback();14 };15 }16 script.src = url;17 document.getElementsByTagName_r("head")[0].appendChild(script);18 }

复制代码

使用方法:

1 loadScript("file1.js", function(){2 alert("File is loaded!");3 });

使文件按顺序加载:

复制代码

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

复制代码

3.XHR脚本注入
使用XMLHttpRequest(XHR)对象将脚本注入到页面中。此技术首先创建一个XHR对象,然后下载javas文件,接着用一个动态<script>元素将javas代码注入页面。

复制代码

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

复制代码

此代码向服务器发送一个获取file1.js文件的GET请求。onreadystatechange事件处理函数检查readyState是不是4,然后检查HTTP状态码是不是有效(2XX表示有效回应,304表示一个缓存响应)。如果收到一个有效的响应,那么就创建一个新的<script>元素,将它的文本属性设置为从服务器接受到的resposeText字符串。这样做实际上会创建一个带有内联代码的<script>元素。一旦新的<script>元素被添加到文档,代码将被执行并准备使用。

这种方法的主要优点是,您可以下载不立即执行的 JavaScript 代码。由于代码返回在<script>标签之外(换句话说不受<script>标签约束),它下载后不会自动执行,这使得您可以推迟执行,直到一切都准备好了。另一个优点是,同样的代码在所有现代浏览器中都不会引发异常。 

此方法最主要的限制是:JavaScript 文件必须与页面放置在同一个域内,不能从 CDN 下载(CDN 指”内容投递网络(Content Delivery Network)”,所以大型网页通常不采用 XHR 脚本注入技术。

总结

减少 JavaScript 对性能的影响有以下几种方法:

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

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

  • 采用无阻塞下载 JavaScript 脚本的方法:

    • 使用<script>标签的 defer 属性(仅适用于 IE 和 Firefox 3.5 以上版本);

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

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

javascript – 触发window.onload事件时加载了哪些外部资源以及资源的加载顺序是什么?

javascript – 触发window.onload事件时加载了哪些外部资源以及资源的加载顺序是什么?

由于单个网页可能包含大量外部资源:外部 javascript,外部CSS,图像,小程序,flash等,通常我的传统智慧告诉我,当所有链接资源下载完成后,会触发window.onload事件(虽然外部资源通常由浏览器的实现下载到多个线程或进程中.通常的情况可能在大多数时间都有效.但是……如果加载序列不是我认为理所当然的,那么一些javascript bug可能会在某个地方蔓延.

在做了一些搜索后,我发现事实并非如此.从这个页面:https://web.archive.org/web/1/http://articles.techrepublic%2ecom%2ecom/5100-10878_11-5214317.html,似乎在onload事件被触发时没有加载图像.但是从window.onload vs <body onload=””/>开始,在我看来,当onload被触发时,图像被加载了.从这个链接http://forums.mozillazine.org/viewtopic.php?f=25&t=413504&start=0&st=0&sk=t&sd=a对我来说更加困惑.

所以我的第一部分问题是:当window.onload被触发时,是否所有资源都被加载了?

问题的另一个密切相关的部分是:在window.onload被触发之前,资源加载顺序是什么?我知道内部资源,如内部javascript或css,加载顺序是从页面顶部到底部(除非在IE中,使用延迟脚本,如此处所示Getting notified when the page DOM has loaded (but before window.onload)).
但是外部javascript和css资源呢?例如,如果我像这样写我的页面:

<external stylesheet...>
 <external javascript #1...>
 <external javascript #2...>
 <script>
  .....
  window.onload=....
  </script>

假设“外部javascript#2”中的函数调用“外部javascript#1”中的函数,我能确定它总是有效吗?另外如果window.onload调用“外部javascript#1”中的函数也可以按预期工作?

您可以说资源加载顺序以及何时触发window.onload事件取决于浏览器实现,如What is the event precedence in JavaScript?所述.但我仍然想知道公众中是否有规范或惯例.那么请你转介我的资源或告诉我事实以清除我的困惑?

解决方法

查看 Cuzillion.它由雅虎表现团队的Steve Souders编写,用于评估这些内容.

它归结为:浏览器按照文档中遇到的顺序加载脚本,并在下载每个脚本时停止所有其他加载.其他资源(css / images)是异步加载的,您无法确定它们何时完成.

加载文档及其脚本/样式/图像资源时会触发onload事件,但如果在加载页面时正在执行任何javascript,则可能不希望等待图像.相反,使用类似jQuery的“ready”事件或通过在body的末尾放置一个脚本标记来激活你自己的“Domready”事件:

<body>
    <!-- your page contents here -->
    <script type="text/javascript">
        // DOM is ready,do scripty stuff Now
        Domready();
    </script>
</body>

Javascript 加载和执行-性能提高篇_javascript技巧

Javascript 加载和执行-性能提高篇_javascript技巧

Js的阻塞性
Javascript 在浏览器中的性能问题,可能是最重要的可用性问题
Js的阻塞性 浏览器用单一进程来处理UI进程和Js的执行
不管是内嵌的还是外链的,下载并立即执行 因为它有可能会修改页面

页面生存周期的概念
瀑布图中看到了下载时间和executing time
在head中加入script 和link body加载到前不会输出任何东西,因此会看到空白页面
复制代码 代码如下:



页面在到script时,不知道script里会做什么,所以必然阻塞,等它执行
Ie8 等浏览器能并行下载多个js等资源,但是还是对下载图片有影响
结论, 将脚本放在底部加载

组织脚本
目标 最小化迟延时间
问题
引用多个script文件 内嵌多个script标签
每个http请求都会带来性能上的开销
紧跟在link 后的script 是个错误 它会等待css的加载,以求获得最精准的描绘
结论:减少script标签的数量
将多个js文件合并成一个,打包工具
yahoo的合并处理器

无阻塞的脚本
js倾向于阻止浏览器的某些处理过程,如http请求处理和界面更新.这是最重要的性能问题。于是要减少js文件大小和限制http请求数
但是功能丰富与代码量之间的矛盾, 合并成单个文件却体积大会锁死浏览器一大段时间,于是我们需要逐步加载javascript文件
重点:在页面加载完成后加载javascript文件

1、延迟加载脚本
defer 属性
如果要下载的js文件不会进行dom操作,那么defer属性有很大的用处,它能让文件并行下载,并不会立即执行,而会在onload事件后再执行,适用于任何script标签
2、动态加载脚本
一个function
复制代码 代码如下:

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 {
script.onload = function(){
callback();
}
}
}

这里要注意浏览器的兼容性,删除事件,
如果有多个文件需要这样加载,那么可以
复制代码 代码如下:

loadScript(''file1.js'', function(){
loadScript(''file2.js'',function(){
})
});

3、XHR脚本注入
复制代码 代码如下:

var xhr = new XMLHttpRequest();
xhr.open(''get'',''file1.js'',true);
xhr.onreadystatechange= function(){
  if(xhr.readyState == 4) {
    if(xhr.status >= 200 && xhr.status var oScript = document.createElement(''script'');
     oScript.text = xhr.responseText;
     document.body.appendChild(oScript);
}
}
}

304表示从缓存取 text将ajax的内容放入text

它的优点
兼容性好,异步, 缺点:不能扩域 不能从cdn取内容

JavaScript 在各个浏览器中执行的耐性_javascript技巧

JavaScript 在各个浏览器中执行的耐性_javascript技巧

IE:执行超过500W条JScript引擎语句出现提示。
Firefox:执行超过10秒出现提示。
Safari:执行超过5秒出现提示。
Opera:无论执行多久都不会出现提示,最有耐性。
Chrome:执行超过约8秒(估计值)出现提示。
注:当弹出类似alert的模式对话框的时候,是不计时。

Web开发的时候,经常会遇到的一种情况就是浏览器提示脚本运行时间过长,停止还是继续,无论你选择什么,相信你都会想尽一切办法让这个对话框远离你的用户们。可你是否知道,这些不同的浏览器究竟是如何判断,哪些脚本处于“失控”状态么?本文作者,就从Internet Explorer、Firefox、Safari、Chrome和Opera五种浏览器,分析了这个情况出现的原因。

【原文标题】What determines that a script is long-running?
【原文作者】Nicholas C. Zakas

以下是对原文的翻译:

Web开发者经常遇到并必须及时处理的问题就是“提示脚本运行时间过长的提示框”(或者称为“失控脚本提示”),这些令人讨厌的对话框会在你的脚本 执行时间过长的时候出现。对于Web开发者的基本准则就是,无论什么时候,都不要让用户看到这些对话框,因为这会给人一种代码缺乏结构化的印象,更简单的 说,你的代码负担太重了。

用Brendan Eich(JavaScript的发明人)的话来讲,如果JavaScript运行的时间需要用秒来计算,一定是什么地方搞错了。我个人可以忍受的上限可 能更小一些,不论什么脚本,在任何时间、任何浏览器上执行,都不应该超过100毫秒。如果实际执行的时间长于这个底限,一定要将进程分解成若干更小的代码 段。

另外,其实很少有人真正意识到究竟是什么原因导致脚本在不同的浏览器中运行时间过长,连我自己都没有深究过。所以我决定坐下来好好研究一下,我们究 竟会在什么情况才会看到那个讨厌的对话框。判断脚本是否失控,无外乎就两种方法。一种是根据执行了多少条语句,一种是判断脚本执行花费的时间。各个浏览器 判断脚本失控的具体方法会有略微的不同。

Internet Explorer

Internet Explorer判断一个脚本是否失控,主要通过JScript引擎执行语句的总数来判断。默认情况下,这个上限是500万条语句,这个值是可以通过注册表修改的。当你的脚本执行的语句数量超过这个限制,你就会看到下面的窗口。

IE Dialog: A script on this page is causing Internet Explorer to run slowly. If it continues to run, your compute may become unresponsive.

这个对话框提示:“这个页面上有一段脚本导致Internet Explorer运行缓慢,如果你继续运行,你的计算机可能会变为无响应状态”。要不是追求技术上的准确性,这样说确实有点过了。对话框有两个选项,要么 停止脚本执行,要么允许脚本继续运行。当这个对话框显示的时候,脚本已经被完全停止了。如果你选择继续运行脚本,就会重新计算当前执行的语句数,也就是 说,如果这个数值再次达到上限时,你会再次看到这个对话框。

Firefox

Firefox是根据脚本引擎持续执行代码的时间来判断一段脚本是否失控。默认的上限是10秒,可以通过about:config页面来修改这个值。这里需要注意的是,当弹出类似alert的模式对话框的时候,是不计时的。当浏览器执行脚本的时间达到这个上限,Firefox就会显示类似下面的对话框:

Firefox Dialog: A script on this page may be busy, or it may have stopped responding. You can stop the script now, open the script in the debugger, or let the script continue.

Firefox的对话框提示:“这个页面的一段脚本目前运行忙,或者这段脚本已经停止响应。你可以停止执行这段脚本,并在调试器中打开这段脚本,或 者保持这段脚本继续运行”。更清楚的描述了遇到的问题,并且没有IE说的那么恐怖。在这个对话框上可以执行三种操作:停止脚本执行、调试脚本或者让脚本继 续运行。和Internet Explorer一样,当运行脚本继续运行以后,对持续运行脚本时间的统计就会重置。调试脚本按钮,只有在你安装了Firebug,并在该页面激活了调试 的时候才会出现。执行调试脚本操作后,可以显示执行时间过长的代码段的具体位置。

Safari

Safari同样根据脚本引擎持续执行脚本的时间来判断,当我对Webkit的源代码进行反复研究后,发现默认的超时时间是5秒,一旦达到这个上限,就会给出下面的对话框提示:

Safari Dialog: A script on the page [url] is making Safari unresponsive. Do you want to continue running the script, or stop it?

对话框提示:“在页面url上的脚本让Safari失去响应,你是要继续运行脚本还是终止脚本”。同样的,对于用户来说,也不是什么可怕的提示。在Safari中,可以关闭失控脚本的检测功能

Chrome

Chrome在跟踪技术上有点狡猾,失控脚本检测功能似乎和tab的事故控制(crash control)关联到一起。我仔细看了源代码,却没有找到具体的限制,但基本确定的是,这个限制是以时间为基础的,估计在10秒左右(要么是5秒,要么 是10秒,总要和Safari或者Firefox看齐么)。我正在联系Chrome项目组中的朋友,看看能不能得到确定的信息。尽管如此,如果网页中存在 失控的脚本,用户还是会看到下面的对话框:

Chrome Dialog: The following page(s) have become unresponsive. You can wait for them to become responsive or kill them.

毫无疑问,Chrome的提示比起其他浏览器来说,显得都更加严重。点击“Wait”按钮,脚本会继续运行,直到达到下一个上限为止,也可以点击“Kill pages”,直接关闭该页面在内存中的所有信息,并用一个空白页取而代之。

Opera

Opera的情况比较有趣:他貌似没有针对失控脚本的相应限制。我运行了几个很长的测试,甚至花了几分钟,而在这个过程中,浏览器一直可以正常响应,这很出我的意料之外。我不是很确定,对于现在的情况来说,这个方法是好是坏,但至少它生效了,不是么?

一些建议

无论你的用户使用什么浏览器,都不应该在任何时候看到类似的提示。在你的网站或者Web应用程序作为产品发布之前,做一些常规的性能测试是非常有必要的。在这方面有很多工具可以加以利用,比如Firebug''s profiler(只支持Firefox)、YUI Profiler (支持全部浏览器)或者Internet Explorer 8''s Profiler。 你应该毫不犹豫地将那些执行时间超过100毫秒的脚本找出来,哪怕这些脚本只是在某些浏览器上运行不畅,这些脚本包含了一些需要执行很长时间的代码段,而 这些代码应该通过性能检测工具进行重新评估。确保你不是使用Chrome作为测试的底线,因为Chrome在执行JavaScript的速度上比其他浏览 器要高出一个数量级(和Firefox 3.1还有最新的WebKit Nightly相当)。最好使用Internet Explorer作为测试的底线,然后再测试其他浏览器,因为无论什么时候,IE的JavaScript引擎都是最慢的,当在IE上修复问题以后,十有八 九在其他浏览器上也可以正常运行了。

今天的关于JavaScript 的性能优化:加载和执行以及动态引入的外部 JS 文件在各浏览器中的加载顺序不一致的分享已经结束,谢谢您的关注,如果想了解更多关于javacript加载和执行、javascript – 触发window.onload事件时加载了哪些外部资源以及资源的加载顺序是什么?、Javascript 加载和执行-性能提高篇_javascript技巧、JavaScript 在各个浏览器中执行的耐性_javascript技巧的相关知识,请在本站进行查询。

本文标签:

上一篇第三方登录之QQ登录 js_SDK(java第三方登录qq登录)

下一篇js byte转KB,MB,GB,TB代码(js byte转string)