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 代码并注入页面中。
时间: 2024-11-10 11:09:41

javacript加载和执行的相关文章

程序的加载和执行(六)——《x86汇编语言:从实模式到保护模式》读书笔记26

程序的加载和执行(六)--<x86汇编语言:从实模式到保护模式>读书笔记26 通过本文能学到什么? NASM的条件汇编 用NASM编译的时候,通过命令行选项定义宏 Makefile的条件语句 在make命令行中覆盖Makefile中的变量值 第13章习题解答 复习如何构造栈段描述符 我们接着上篇博文说. 在我修改后的文件中,用到了条件汇编. 比如: %ifdef DEBUG put_core_salt: ;打印内核的符号 ... ... put_usr_salt: ;打印用户的符号 ... .

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

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

加载和执行

有阻塞的脚本   js脚本阻塞原因? 大多数浏览器使用单一进程处理用户界面更新(页面解析,页面渲染,用户交互)和JavaScript脚本代码执行.所以同一时刻只能做其中一件事.这样做是显而易见的,因为脚本代码很可能包含了dom处理的代码. 反应到代码上就是,当script标签出现时,无论代码是内嵌还是通过外链加载,都需要等待js代码加载(内部代码就直接读取)并执行完成,才能继续解析渲染页面. 注释:外部脚步有两个例外: 如果 async="async":脚本加载完成可用后,会相对于页面

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

最近在研究HTML页面中JavaScript的执行顺序问题.在JavaScript中,定义一个方法或者函数有很多方式,最常见的有2中,function语句式与函数直接量方式. 对于function语句式,解释器会优先解释.即加载了这个js文件后,会扫描一下所有的js代码,然后把该优先执行的东西先执行了,然后再从上到下按顺序执行.所以,定义的代码可以在执行的代码后边.就跟C#中的方法定义一样.解释器已经记住了这个方法,知道在内存中的哪里,用的时候直接去取就行了. C#语言是,对象中的属性与方法具有

怎么样加快JavaScript加载和执行效率

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

页面加载完毕执行多个JS函数

通常我们需要在打开页面时加载脚本,这些脚本必须在页面加载完毕后才可以执行,因为这时候DOM才完整,可以利用window.onload确保这一点,如:window.onload=firstFunction;这脚本的意思是在页面完毕后执行firstFunction函数,但当有很多个函数需要在页面加载时执行呢?可能有人说可以这样:window.onload=firstFunction;window.onload=secondFunction; 但这样的话只会执行secondFunction函数. Si

程序的加载和执行(三)——《x86汇编语言:从实模式到保护模式》读书笔记23

程序的加载和执行(三)--读书笔记23 接着上次的内容说. 关于过程load_relocate_program的讲解还没有完,还差创建栈段描述符和重定位符号表. 分配栈空间与创建栈段描述符 462 ;建立程序堆栈段描述符 463 mov ecx,[edi+0x0c] ;4KB的倍率 464 mov ebx,0x000fffff 465 sub ebx,ecx ;得到段界限 466 mov eax,4096 467 mul dword [edi+0x0c] 468 mov ecx,eax ;准备为

浏览器环境下JavaScript脚本加载与执行探析之动态脚本与Ajax脚本注入

在<浏览器环境下JavaScript脚本加载与执行探析之defer与async特性>中,我们研究了延迟脚本(defer)和异步脚本(async)的执行时机.浏览器支持情况.浏览器bug以及其他的细节问题.而除了defer和async特性,动态脚本和Ajax脚本注入也是两种常用的创建无阻塞脚本的方法.总的来看,这两种方法都能达到脚本加载不影响页面解析和渲染的作用,但是在不同的浏览器中,这两种技术所创建的脚本的执行时机还是有一定差异,今天我们再来探讨一下通过动态脚本技术和Ajax注入的脚本在这些方

页面加载完执行的代码

<script  defer="defer">    alert("页面加载完我才执行的")</script> <script language="javascript" type="text/javascript">     function(){alert("操作成功") var yh='<%Session["user"] %>';}