手机版

JavaScript性能优化摘要的加载和执行

时间:2021-09-07 来源:互联网 编辑:宝哥软件园 浏览:

无论当前的JavaScript代码是嵌入的还是在外部链文件中,页面的下载和呈现都必须停止,等待脚本执行。JavaScript执行过程花费的时间越长,浏览器等待响应用户输入的时间就越长。浏览器在下载和执行脚本时被阻止的原因是脚本可能会改变页面或JavaScript的命名空间,从而影响后续页面的内容。一个典型的例子是在页面中使用document.write(),如清单1所示

清单1 JavaScript代码嵌入示例

htmlhead titleSource示例/title/headbody p脚本类型=' text/JavaScript ' document . write(' Today is '(new Date())。todaytestring());/script/p/body/html当浏览器遇到脚本标签时,当前的html页面无法知道JavaScript是否会在p标签中添加内容、引入其他元素,甚至移除标签。因此,浏览器将停止处理页面,首先执行JavaScript代码,然后继续解析和呈现页面。在加载带有src属性的JavaScript的过程中也会发生同样的情况。浏览器必须花时间下载外部链接文件中的代码,然后解析并执行它。在这个过程中,页面渲染和用户交互被完全阻断。

脚本位置

HTML 4规范指出,脚本标签可以放在HTML文档的头部或主体,并允许多次出现。Web开发人员一般习惯在头部加载JavaScript,然后使用链接标签在头部加载CSS文件或其他页面信息。例如,清单2

清单2低效脚本位置的例子

htmlhead titleSource示例/标题脚本类型=' text/JavaScript ' src=' http : script 1 . js '/脚本脚本类型=' text/JavaScript ' src=' http : script 2 . js '/脚本类型=' text/JavaScript ' src=' http 3360 script 3 . js '/脚本链接rel='样式表' type=' text/CSS ' href=' styles . CSS '/head dy pHello world!/p/body/html然而,这种传统的方法隐藏了严重的性能问题。在清单2的示例中,当浏览器解析脚本标签(第4行)时,浏览器将停止解析以下内容,并首先下载脚本文件并执行其中的代码,这意味着无法加载以下styles.css样式文件和body标签,因此无法自然呈现页面,因为body标签无法加载。因此,在JavaScript代码完全执行之前,页面是空白的。图1描述了页面加载过程中脚本和样式文件的下载过程。

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

我们可以发现一个有趣的现象:第一个JavaScript文件开始下载,同时它会阻止页面上其他文件的下载。此外,脚本1的完成之间存在延迟。JS下载和script2.js下载的开始,这正是script1.js文件的执行过程。每个文件必须等到下载并执行前一个文件。在逐一下载这些文件的过程中,用户会看到一个空白页。

允许从IE 8、Firefox 3.5、Safari 4和Chrome 2并行下载JavaScript文件。这是一个好消息,因为脚本标签在下载外部资源时不会屏蔽其他脚本标签。不幸的是,JavaScript下载过程仍然会阻止其他资源的下载,例如样式文件和图片。虽然脚本的下载过程不会互相影响,但是页面还是要等所有的JavaScript代码下载执行完毕后才能继续。因此,尽管最新的浏览器通过允许并行下载来提高性能,但问题并没有完全解决,脚本阻塞仍然是一个问题。

因为脚本会阻挡页面上其他资源的下载,所以建议尽可能将所有脚本标签放在body标签的底部,最大限度的减少对整个页面下载的影响。例如,清单3

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

htmlhead titleSource示例/title link rel='样式表' type=' text/CSS ' href=' styles . CSS '/head body pHello world!/p!-高效脚本定位示例-脚本类型=' text/JavaScript ' src=' http : script 1 . js '/脚本脚本类型=' text/JavaScript ' src=' http 3360 script 2 . js '/script type=' text/JavaScript ' src=' http : script 3 . js '/脚本/正文/HTML显示了在HTML文档中放置脚本标记的建议位置。虽然下载一个脚本会屏蔽另一个脚本,但是页面的大部分内容已经下载并显示给用户了,所以页面下载不会显得太慢。这是优化JavaScript的第一条规则:把脚本放在最下面。

组织脚本

由于每个脚本标签在最初下载时都会阻止页面呈现,因此减少页面中包含的脚本标签数量有助于改善这种情况。这不仅适用于外部链脚本,也适用于嵌入式脚本的数量。浏览器在解析HTML页面的过程中,每次遇到脚本标签,都会因为脚本执行而造成一定的延迟,因此将延迟时间降到最低,显然会提升页面的整体性能。

在处理链外JavaScript文件时,这个问题略有不同。考虑到HTTP请求会带来额外的性能开销,下载一个100Kb的文件比下载五个20Kb的文件要快。也就是说,减少页面上的离线脚本数量将提高性能。

通常,大型网站或应用程序需要依赖几个JavaScript文件。您可以将多个文件合并成一个文件,因此只需要引用一个脚本标记,这样可以降低性能消耗。文件合并可以通过离线打包工具或一些实时在线服务来实现。

需要注意的是,在引用外部样式表的链接后放置一个嵌入式脚本将导致页面阻塞并等待样式表被下载。这是为了保证嵌入式脚本在执行时能够得到最准确的样式信息。因此,建议不要将嵌入的脚本直接放在链接标签后面。

非阻塞脚本

减少JavaScript文件的大小和限制HTTP请求的数量在功能丰富的Web应用程序或大型网站中并不总是可行的。Web应用程序的功能越丰富,需要的JavaScript代码就越多。虽然下载单个大的JavaScript文件只会产生一个HTTP请求,但是会长时间锁定浏览器。为了避免这种情况,需要通过一些特定的技术逐步将JavaScript文件加载到页面中,不会在一定程度上阻塞浏览器。

非阻塞脚本的秘密是在页面加载后加载JavaScript代码。这意味着在窗口对象的onload事件被触发后下载脚本。有很多方法可以达到这种效果。

延迟加载脚本

HTML 4为脚本标签定义了一个扩展属性:defer。“延迟”属性表示该元素中包含的脚本不会修改DOM,因此代码可以安全地延迟执行。延期属性只有IE 4和Firefox 3.5的浏览器支持,所以不是理想的跨浏览器解决方案。在其他浏览器中,defer属性被直接忽略,因此默认情况下处理脚本标记,这意味着阻塞。但是,如果您的目标浏览器支持,这仍然是一个有用的解决方案。清单4是一个例子

清单4如何使用延迟属性的示例

script type=' text/JAVAScript ' src=' http : script 1 . js ' delay/script带有delay属性的标记可以放在文档中的任何位置。当页面被解析为脚本标记时,相应的JavaScript文件将被下载,但是直到加载了DOM,也就是在onload事件被触发之前,才被执行。下载带有defer属性的JavaScript文件时,不会阻塞浏览器中的其他进程,因此这类文件可以和其他资源文件并行下载。

无论是嵌入脚本还是链接脚本,任何具有延迟属性的脚本元素都不会在DOM加载完成之前执行。清单5中的示例显示了延迟属性如何影响脚本行为:

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

htmlhead titleScript Defer示例/title/headbody脚本类型=' text/JavaScript ' delay alert(' delay ');/script script type=' text/JavaScript ' alert(' script ');/script script type=' text/JavaScript ' window . onload=function(){ alert(' load ');};/script/body/html这段代码在页面处理过程中会弹出三次对话框。不支持延迟属性的浏览器的弹出顺序是“延迟”、“脚本”和“加载”。在支持延迟属性的浏览器上,弹出顺序是“脚本”、“延迟”和“加载”。请注意,具有defer属性的脚本元素不会在第二个之后执行,而是在onload事件被触发之前被调用。

如果你的目标浏览器只包括互联网浏览器和火狐3.5,那么延期脚本真的很有用。如果您需要跨域支持多个浏览器,那么有更一致的实现。

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

动态脚本元素

文档对象模型(DOM)允许您使用JavaScript动态创建HTML的几乎所有文档内容。像页面上的其他元素一样,脚本元素可以通过标准的DOM函数轻松创建:

清单6通过标准DOM函数创建脚本元素

var script=document . createelement(' script ');script . type=' text/JavaScript ';script . src=' script 1 . js ';document . getelementsbytagname(' head ')[0]。appendChild(脚本);新的script元素加载script1.js源文件。一旦元素被添加到页面,该文件就开始下载。这项技术的关键点在于,无论从哪里开始下载,文件的下载和运行都不会阻塞其他页面处理过程。您甚至可以将这段代码放在头部分,而不影响页面代码的其余部分(下载文件的HTTP连接除外)。

当使用动态脚本节点下载文件时,返回的代码通常会立即执行(除了Firefox和Opera,它们会等待所有以前的动态脚本节点完成执行)。当脚本“自运行”时,这种机制工作正常,但如果脚本只包含页面上其他脚本调用的接口,就会出现问题。在这种情况下,您需要跟踪脚本下载是否完成并准备就绪。您可以使用动态脚本节点发布事件来获取相关信息。

火狐、Opera、Chorme和Safari 3将在收到脚本节点后发出onload事件。您可以收听此事件以获得脚本准备就绪的通知:

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

var script=document . createelement(' script ')script . type=' text/JavaScript ';//火狐,Opera,Chrome,Safari 3 Script . onload=function(){ alert(' Script loaded!');};script . src=' script 1 . js ';document . getelementsbytagname(' head ')[0]。appendChild(脚本);Internet Explorer支持另一个实现,它会发出readystatechange事件。Script元素有一个readyState属性,它的值会随着下载外部文件的过程而改变。readyState有五个值:

1.“未初始化”:默认状态

2.“加载”:下载开始

3.“已加载”:下载完成

4.“交互式”:下载已完成,但尚未提供

5.“完成”:所有数据已准备好

根据微软文档,readyState的所有这些值可能不会出现在脚本元素的生命周期中,但它并不指示将始终使用哪些值。实际上,我们最感兴趣的是“加载”和“完成”状态。这两个就绪状态值代表的最终状态在Internet Explorer中不一致。有时脚本元素会得到“loader”,但永远不会“complete”,但在其他情况下,会出现“complete”而不是“loaded”。最安全的方法是在readystatechange事件中检查这两种状态,当其中一种出现时,删除readystatechange事件句柄(以确保事件不会被处理两次):

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

var script=document . createelement(' script ')script . type=' text/JavaScript ';//Internet explorers script . onreadystatechange=function(){ if(script . readystate==' loaded ' | | script . readystate==' complete '){ script . onreadystatechange=null;警报('脚本已加载');}};script . src=' script 1 . js ';document . getelementsbytagname(' head ')[0]。appendChild(脚本);在大多数情况下,您希望调用一个函数来实现动态加载JavaScript文件。以下功能封装了标准实施和IE实施所需的功能:

清单9由一个函数封装

函数loadScript(url,回调){ 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;回调();} };} else {//Others script . onload=function(){ callback();};} script.src=urldocument . getelementsbytagname(' head ')[0]。appendChild(脚本);}该函数接收两个参数:JavaScript文件的URL和接收JavaScript时触发的回调函数。属性检查用于决定监视哪些事件。最后,设置src属性并将脚本元素添加到页面中。loadScript()函数的用法如下:

清单10如何使用LoadScript()函数

loadScript('script1.js ',函数(){ alert('文件已加载!');});您可以在页面上动态加载许多JavaScript文件,但请注意,浏览器不保证加载文件的顺序。在所有主要浏览器中,只有火狐和Opera确保脚本按照您指定的顺序执行。其他浏览器会按照服务器返回的顺序下载并运行不同的代码文件。您可以连接下载操作以确保它们的顺序,如下所示:

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

loadScript('script1.js ',function(){ load script(' script 2 . js ',function(){ load script(' script 3 . js ',function(){ alert('所有文件都已加载!');});});});这段代码在加载script2.js之前等待script1.js可用,在加载script3.js之前等待script2.js可用.这种方法虽然可行,但是如果要下载和执行的文件很多,还是比较麻烦的。如果多个文件的顺序很重要,最好以正确的顺序将这些文件连接成一个文件。独立文件可以一次下载所有代码(因为这是异步完成的,所以使用大文件没有损失)。

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

使用XMLHttpRequest(XHR)对象

这项技术首先创建一个XHR对象,然后下载JavaScript文件,然后用动态脚本元素将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(脚本);} }};xhr . send(null);这段代码向服务器发送GET请求以获取script1.js文件。onreadyStatechange事件处理程序检查readystate是否为4,然后检查HTTP状态代码是否有效(2XX表示有效响应,304表示缓存响应)。如果收到有效的响应,将创建一个新的脚本元素,并将它的文本属性设置为从服务器收到的响应文本字符串。这样做实际上是用内联代码创建一个脚本元素。一旦新的脚本元素被添加到文档中,代码将被执行并准备使用。

这种方法的主要优点是可以下载不立即执行的JavaScript代码。由于代码在脚本标记之外返回(换句话说,它不受脚本标记的约束),所以下载后不会自动执行,这允许您推迟执行,直到一切准备就绪。另一个优点是相同的代码不会在所有现代浏览器中抛出异常。

这种方法的主要限制是JavaScript文件必须和页面放在同一个域中,不能从CDN (CDN指的是‘内容传递网络’)下载,所以XHR脚本注入技术通常不用于大型网页。

添加一些您通常使用的功能

函数loadJs(url,回调,字符集){ var head=document . GetElementsBytagname(' head ')[0];var script=document . createelement(' script ');if(!charset)script . charset=' utf-8 ';script.src=urlscript . onload=script . onreadystatechange=function(){ var f=script . readystate;if (f f!='loaded' f!='complete ')返回;script . on load=script . onreadystatechange=null;head.removeChild(脚本)if(回调){ callback()| | callback };};head.appendChild(脚本);}//js同步加载函数getscripts (I,linkarray,fn){ env | | getenv();var script=document . createelement(' script ');script . type=' text/JavaScript ';script . src=LinkArray[I];var head=document . head | | document . getelementsbytagname(' head ')[0];head.appendChild(脚本);if (env.ie脚本中的' onreadystatechange '!(“可拖动”的碑文){//ie浏览器加载脚本。onreadystatechange=function(){ if(/loaded | complete/。测试(脚本。readystate)){脚本。onreadystatechange=nullif(I===LinkArray . length-1){ if(fn){ fn();} } else { getScripts(i,linkArray,fn);} } };} else { script . onload=function(){ if(I===LinkArray . length-1){ if(fn){ fn();} } else { getScripts(i,linkArray,fn);} };}}//js有依赖关系,然后加载getscripts (0,[' http://caibojian.com/demo/base . js ',' http://caibojian.com/demo/reset . js '],function(){ alert(' callback ');});摘要

有几种方法可以降低JavaScript的性能影响:

将所有脚本标记放在页面的底部,即在/body关闭标记之前,可以确保在脚本执行之前页面已经呈现。

尽可能合并脚本。页面中的脚本标签越少,加载和响应的速度就越快。无论是内嵌脚本还是内嵌脚本都是如此。

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

使用脚本标签的delay属性(仅适用于IE和Firefox 3.5或更高版本);

使用动态创建的脚本元素来下载和执行代码;

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

通过以上策略,需要大量使用JavaScript的网站和应用程序的实际性能可以得到很大的提升。

以上就是JavaScript性能优化总结的加载和执行的全部内容,希望对大家有所帮助。

版权声明:JavaScript性能优化摘要的加载和执行是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。