最大化JavaScript和Ajax性能

在网络的早期,最大限度地提高网页的性能通常意味着避免使用不必要HTML标记,将JavaScript代码的数量保持在最低水平,并大量减少任何图像的文件大小,以使典型的冲浪者不必加载页面时,不必走开去喝杯咖啡。

网络各个方面的进步意味着我们现在面临着一套全新的性能考虑。 尽管DSL和宽带已使许多人更容易访问高速Internet,但我们对加载时间和响应能力的期望也发展到一种状态,在这种状态下,我们在页面上执行操作时会寻找即时结果。 异步JavaScript和XML(Ajax)的出现使开发人员可以在Web应用程序中提供类似于桌面的体验,而无需在响应事件之前加载整个页面。 这样做的好处是显而易见的,但同时也导致了普通Web用户期望所有Web应用程序都具有这种响应能力。 随着移动网络的最新兴起,满足现代网络用户的期望面临着新的挑战,所有这些都需要在目标设备上以较小的屏幕,较少的功率和较慢的连接速度进行。

本文的重点是告知您应该最大限度地提高JavaScript和Ajax Web应用程序性能的注意事项。 本文提供了有关如何以最佳方式处理正在编写的任何新代码(无论是在新应用程序中还是现有应用程序中)的一组准则。 您还将了解用于评估应用程序性能的各种工具和技术。 最后,您将了解不需要提高您现有代码的各种提高性能的方法。

JavaScript和Ajax开发的最佳实践

JavaScript开发的问题之一是,很大一部分编写JavaScript代码的开发人员和Web设计师从未真正从头开始学习JavaScript本身。 他们多年以来在网络上发现的执行特定功能的代码片段通常会积累他们的语言知识。 他们通常会了解如何声明变量,编写条件语句和执行计算,但是他们从未坐下来使用JavaScript语言的全面指南,而是从零开始学习。 如今,这个问题依然存在,因为开发人员急于使用jQuery和YUI之类的库和框架来简化生活。

尽管使用JavaScript库没有错(事实上,我是其中的忠实拥护者。),但是现代开发人员倾向于在他们选择JavaScript框架中成为专家,而不是JavaScript本身。 这样做的问题是,您常常会发现这些开发人员使用的是效率低下的编码实践,有时甚至使用框架功能来执行某些操作,而这些操作使用常规的旧JavaScript会更快。

在本部分中,您将学习一些JavaScript和Ajax开发的最佳实践,特别是在那些非JavaScript开发人员往往忽略的领域。

使用外部JavaScript文件

最大程度地提高JavaScript应用程序性能的黄金法则是,尽可能使用外部JavaScript文件,而不是直接在HTML文件中包含JavaScript代码。 这样做不仅意味着您不必跨多个页面复制JavaScript代码,而且还意味着您JavaScript代码将由网络浏览器缓存,并且在以后每次加载页面时都无需重复加载。 由于外部文件需要将其他HTTP请求发送到服务器,因此第一页的加载速度将大大降低。 但是,在大多数应用程序中,与此相关的性能损失远远超过了后续页面加载所节省的后续性能。

此规则的例外情况是,大多数访问者只能浏览一页。 首选使用内联JavaScript。 或者您需要第一个页面加载的速度要快于后续页面加载的速度。 在他的书中, 高性能网站 (见相关信息中的链接),史蒂夫发声提出后的Onload下载 ,让您保存在HTML文件本身的第一个页面内嵌JavaScript代码的想法,然后动态页面完全加载后,加载后续页面所需的外部JavaScript文件。 但是,在大多数情况下,仅使用外部JavaScript文件就足够了。

何时使用JavaScript框架和库

我全都使用JavaScript框架和库。 它们不仅有助于解决许多跨浏览器的兼容性问题,而且,如果使用得当,它们可以大大加快Web应用程序的开发时间。 话虽如此,使用这些工具时应格外小心,因为它们中的大多数都很大,并且会损害应用程序的性能。

您需要问自己的第一件事是:我真的需要使用框架吗? 我对JavaScript框架的介绍是在几年前,当时我需要在正在开发的Web应用程序中使用Ajax。 我决定使用Prototype框架来简化生活,而不是滚动自己的XMLHttpRequest函数。 该应用程序除了执行Ajax请求和处理来自服务器的响应外,没有使用任何框架,但是我还是使用了它。 对我来说幸运的是,我正在开发的应用程序相对较小,并且只供内部使用,因此性能并不是至关重要的,但是多年以来,我知道使用仅提供Ajax功能的更轻巧的解决方案可能会更好。

未缩小和未压缩的最新版Prototype框架大小为141KB。 与我的应用程序相关的部分代码可能少于2KB,而在我的应用程序中大约有139KBJavaScript代码尚未使用。 这不仅增加了我的应用程序在文件大小方面的加载时间,而且还增加了在浏览器中执行JavaScript代码的执行时间。

总结一下我的观点,现代JavaScript框架和库(例如Prototype,jQuery,Dojo,MooTools,YUI,ExtJS等)都包含大量您可能会或可能不会使用的功能。 如果您仅使用最少的功能集,那么可能值得研究一个更轻便的解决方案。 例如,YUI库允许您默认加载基本框架,然后选择需要在其之上加载的库。

脚本放置和加载

如果您阅读有关HTML的书,则可能建议将所有<script>标记都放置在页面上的<head>元素内。 如果这是您目前对<script>标签位置的理解,请立即从内存中删除它! 在HTML页面顶部附近放置<script>标记将阻止页面呈现,直到JavaScript代码完成加载和执行为止。 如果将它们放在<head>标记内,则在脚本加载和执行之后,页面主体才会真正呈现,这给用户留下了页面加载缓慢的印象。

为了最大程度地提高页面的性能,应尽可能将JavaScript代码放在页面底部附近</ body>标记之前。 这样,网页的其余部分(HTML,CSS,图像,Flash内容等)将在脚本被加载和执行之前全部下载并呈现,给用户带来更快的加载感觉。

如果您的网页或应用程序需要大量JavaScript,那么将所有这些代码放入一个文件中可能会导致下载和执行过程中的漫长等待。 在这些情况下,将您JavaScript代码拆分为多个文件,并在页面加载完成后根据需要动态加载这些文件是有意义的。 LazyLoad JavaScript库旨在提供动态脚本加载,并将处理与跨脚本浏览器在保持脚本执行顺序方面的不一致问题。 有关LazyLoad库的详细信息,请参阅相关主题

最小化Ajax请求

Ajax请求彻底改变了传统Web应用程序的外观,使JavaScript开发人员可以创建高度动态,交互式和响应式的应用程序,就像桌面应用程序所期望的那样。 结果,现在在现代Web应用程序中到处都可以看到Ajax请求。 有时很容易忘记,尽管用户没有看到正在加载的新页面,但是Ajax请求确实执行了整个HTTP请求,这等效于对常规页面加载的请求。 因此,应格外小心,以尽量减少使用的Ajax请求的数量。

分页搜索结果就是一个例子。 我经常看到应用程序使用Ajax请求将搜索结果记录自身带回JSON数组,并使用第二个请求返回数据库中结果的总数,然后将其用于分页逻辑。 清单1清单2展示了这两个请求的基本示例(使用Prototype框架)。

清单1.第一个请求:检索表记录
var url = "get_data.php";
var options = {
	method: "post",
	parameters: {"page":1,"rows":5},
	onSuccess: firstCallbackFunction,
	onFailure: firstCallbackFunction
}
new Ajax.Request(url, options);

清单2显示了第二个检索总记录的请求。

清单2.第二个请求:检索记录总数
var url = "get_count.php";
var options = {
	method: "post",
	parameters: {},
	onSuccess: secondCallbackFunction,
	onFailure: secondCallbackFunction
}
new Ajax.Request(url, options);

清单3清单4展示了两个对应的JSON格式的HTTP响应。

清单3.第一个响应:记录数组
{
	"records": [
		{"id":1,"name":"John","email":"john@example.com"},
		{"id":2,"name":"Mary","email":"mary@example.com"},
		{"id":3,"name":"Tony","email":"tony@example.com"},
		{"id":4,"name":"Emma","email":"emma@example.com"},
		{"id":5,"name":"Alan","email":"alan@example.com"}
	]
}

清单4显示了第二个响应,报告记录的总数。

清单4.第二个响应:记录总数
{"total_records": 95}

分开这两个Ajax请求很浪费资源,因为很容易将它们组合成一个请求,在清单5中产生以下响应。

清单5.有效的响应:总数和记录数组
{
    "total_records": 95,
    "records": [
		{"id":1,"name":"John","email":"john@example.com"},
		{"id":2,"name":"Mary","email":"mary@example.com"},
		{"id":3,"name":"Tony","email":"tony@example.com"},
		{"id":4,"name":"Emma","email":"emma@example.com"},
		{"id":5,"name":"Alan","email":"alan@example.com"}
	       ]
}

这不仅需要减少一个HTTP请求和响应,而且还意味着您可以减少一个服务器端脚本,该脚本提供了对Ajax请求的响应。

该示例是一个非常简单的演示-应用程序越复杂,考虑是否可以减少需要使用的Ajax请求的数量就越重要。

使用变量(正确)

为了尽量减少他们编写的代码行数,许多开发人员常常忽略了变量的使用,在许多情况下,变量可以大大加快特定代码段的执行速度。 以下面的代码为例,该代码将各种样式应用于元素:

清单6.将样式应用于元素(效率低下)
document.getElementById("myField").style.backgroundColor = "#CCC";
document.getElementById("myField").style.color = "#FF0000";
document.getElementById("myField").style.fontWeight = "bold";

在上面的每一行中,浏览器都需要在DOM中搜索ID为myField的元素。 通过将document.getElementById("myField")的结果分配给一个变量,并在每行中使用它,可以提高此操作的效率,而不是三遍,如清单7所示。

清单7.将样式应用于元素(有效)
var myField = document.getElementById("myField");
myField.style.backgroundColor = "#CCC";
myField.style.color = "#FF0000";
myField.style.fontWeight = "bold";

对于经常遍历数组的for循环,通常不使用变量的另一个示例。 拿清单8所示的例子。

清单8.遍历一个for (无效)的数组
for(var i=0; i < myArray.length; i++) {
	//do something
}

该代码效率极低,因为它将需要在每次循环迭代时计算数组myArray的长度。 尽管在小型阵列中这可能并不明显,但是在大型阵列中的后果要大得多。 改善此循环的性能非常简单,甚至不需要额外的代码行(请参见清单9 )。

清单9.使用for (有效)遍历数组
for(var i=0, arrayLength=myArray.length; i < arrayLength; i++) {
	//do something
}

在清单9中,我们已将对myArray.length的引用移至for语句的初始化部分,并将其分配给变量。 这只会在循环的第一次迭代中发生,从而节省了每个后续迭代的宝贵执行时间。

使用DOM

通常,遍历和操作DOM会对Web应用程序造成沉重的性能负担。 问题是,与DOM交互绝对是必不可少的,以便为您的应用程序提供高度响应的丰富接口。 DOM操作的一个特殊问题是浏览器需要在屏幕上重新排列和重新绘制(基本上重新绘制)受影响的元素。 因此,减少代码导致重排和重新绘制的次数非常重要。

清单7中的示例为例,在该示例中,我们获取了一个ID为myField的元素,并对其应用了三个单独的样式属性。 考虑到我们所做的微不足道的更改,这将导致三个单独的重排和重画,效率很低。 考虑到所有这些动作都可以组合成一个单独的操纵动作,因此效率特别低下。 清单10显示了一个更有效的示例。

清单10.通过合并更改来提高DOM操作性能
var myField = document.getElementById("myField");
myField.style.cssText = "background-color: #CCC; color: #FF0000; font-weight: bold";

通过组合样式更改,结果是一次重制和重新绘制,并由此扩展了此应用程序中的响应速度和性能。

本节介绍了许多JavaScript和Ajax开发最佳实践,可帮助您在应用程序中提供最佳性能。 此列表绝不是详尽无遗的。 请参阅相关信息的链接,优秀文章,教程和书籍专用于高亮高性能JavaScript中的最佳实践。

衡量绩效

在衡量Web应用程序的性能时,可以使用一系列工具,这些工具使您可以从Web浏览器本身分析应用程序的速度和加载时间。 这些工具通常提供用于分析一段JavaScript代码并基本上计算执行时间的功能。 它们还提供了一种方法,可根据页面加载引起的页面请求,加载的静态资源(图像,外部样式表和JavaScript文件)的大小等分析页面加载生成的网络流量。

在本节中,您将学习各种可用于分析和分析JavaScript应用程序中网络活动的工具。

萤火虫

Firebug是Mozilla®Firefox®的浏览器扩展,可为Web开发人员提供大量有用的功能。 这些功能包括JavaScript控制台,DOM操作和选择,脚本调试,DOM源浏览,概要分析,网络分析等等。

要在Firebug中执行JavaScript分析,请导航至“ 控制台”选项卡,然后单击“ 配置文件”按钮。 现在,您可以与页面上启用了JavaScript / Ajax的各种功能进行交互,Firebug会为您计时。 Firebug还提供了一个控制台API与探查器进行交互。 图1显示了Firebug分析器生成的示例报告的屏幕截图。

图1. Firebug分析器样本报告
Firebug探查器样本报告的屏幕快照,看起来类似于excel电子表格。顶部有“控制台”,“ HTML”,“ CSS”,“脚本”,“ DOM”和“ Net”选项卡。

要使用Firebug的网络分析工具,只需单击“ 网络”选项卡。 它将显示所有发出的HTTP请求,收到的响应代码和消息,从中检索到的域,文件大小以及页面加载的时间轴。 您甚至可以深入研究请求,以查看发送的HTTP标头,收到的响应以及相关文件的缓存信息。 图2显示了网络流量的Firebug输出示例。

图2. Firebug Net面板示例报告
Firebug Net面板示例报告的屏幕截图,显示“全部”。

Safari Web检查器和Chrome开发者工具

Safari®Web Inspector和Chrome®Developer Tools提供了与Firebug类似的功能,尽管它们看起来更漂亮。 在Safari中,通过转到工具栏上的Develop菜单(您可能需要在Safari偏好设置中启用它)并选择Show Web Inspector来打开Web Inspector 。 单击检查器窗口顶部的“ 轮廓”选项卡,然后单击左下方的圆圈记录图标以开始分析。 再次单击该图标可停止分析并生成报告。 图3显示了此报告的样例。

图3. Safari Web Inspector探查器示例报告
Safari Web Inspector探查器样本报告的屏幕截图

要分析应用程序的加载时间和HTTP请求,请单击“检查器”窗口顶部的“ 资源”按钮。 然后,您可以选择查看按时间或大小使用的网络资源的图表。 漂亮的输出示例如图4所示

图4. Safari Web Inspector资源示例报告
Safari Web Inspector资源示例报告的屏幕截图,顶部带有颜色编码和条形图。

Google Chrome浏览器的开发者工具与Safari的浏览器相同(两个浏览器均基于WebKit),但可以在“开发者”菜单中找到“开发者工具”。

Internet Explorer开发人员工具

InternetExplorer®8也附带了一组开发人员工具。 按F12键启动“开发人员工具”窗口。 Internet Explorer的工具提供了Firebug之类的功能的子集,但它确实包括一个不错的分析器,用于衡量JavaScript函数的性能。 在开发人员工具中,单击“ 探查器”选项卡,然后单击“ 开始分析”按钮以开始对应用程序进行分析。 使用您的应用程序执行您要测试的功能,然后单击“ 停止分析”以生成报告。 它的外观应类似于图5所示。

图5. Internet Explorer开发人员工具探查器样本报告
Internet Explorer开发人员工具探查器样本报告的屏幕截图,该样本看起来类似于Excel电子表格。

不幸的是,Internet Explorer的开发人员工具不包含网络分析器。 如果要使用Internet Explorer分析应用程序中的网络流量,则可以使用Fiddler工具,该工具可用于几乎所有发出HTTP请求的应用程序,包括Internet Explorer。 有关Fiddler的更多信息,请参阅参考资料

YUI探查器

YUI Profiler是用JavaScript编写的代码分析器工具。 与Web浏览器(或诸如Firebug之类的浏览器扩展程序)附带的探查器工具不同,YUI探查器是非可视的,必须包含在您的网页中。 使用它的好处是,您可以专门针对希望分析的应用程序区域(而不仅仅是执行的任何功能)。 YUI Profiler允许您注册函数,类构造函数和要测量的对象。 然后,您可以生成由探查器生成的输出的报告,可以选择提供过滤器功能,该功能可以让您进一步查明要测量的目标区域。 配置文件报告以JSON格式生成,使您可以轻松地将结果导出到自己的应用程序中,在其中可以生成各种表格和基于图形的数据视图。

YUI Profiler的主要优点之一是它并不特定于任何特定的浏览器。 它甚至可以在具有A级浏览器的移动设备上运行,例如在Apple®iPhone®,Android®设备和Nokia®N95等设备上找到的基于WebKit的浏览器。

有关YUI Profiler的更多信息,请参阅参考资料

慢速

YSlow是Firefox扩展,可插入Firebug扩展,从而为网页的加载和执行提供评分。 它提供了有关JavaScript和Web页面性能各个方面的总体评分和详细分析。 在我的测试中,我在发出91个HTTP请求的页面上运行YSlow,该页面收到的等级为C。 它提供了一个有用的技巧,该页面包含10个外部JavaScript脚本,建议我尝试将它们组合为一个。

它评分的其他方面包括添加到期标头,使用gzip,将JavaScript放在页面底部,最小化JavaScript和CSS,删除重复JavaScript和CSS代码等等。 图6显示了YSlow产生的输出示例。

图6. YSlow样本报告
YSlow样本报告的屏幕截图,显示了D的总体等级。页面左侧是测试的各个领域的等级。

在不更改代码的情况下提高性能

既然您已经测量了应用程序的性能并确定需要提高其性能,您可能会认为您需要进行深入研究并修改应用程序的源代码。 是的,在某些方面这可能是必需的,但是有几种方法可以改善JavaScript和Ajax性能,而无需根本不更改应用程序代码。

合并JavaScript源文件

如果您有大型JavaScript应用程序,则可能会将许多外部JavaScript源文件加载到页面中以提供各种功能。 也许您正在使用诸如jQuery之类的框架以及许多插件,这些框架添加了在jQuery本身之上构建的其他功能。 您可能还拥有自己的应用程序的基础JavaScript代码,这些代码本身可能会拆分为多个文件。

尽管将JavaScript分成单独的文件有很多优点,但这对性能产生了严重影响。 对于您在Web应用程序中加载的每个外部JavaScript文件,都会执行一个单独的HTTP请求,这将大大增加脚本的加载和执行时间。 为了减少请求数量,强烈建议您尽可能合并JavaScript源文件。 当然,如果仅在一页上需要特定的插件,则不应将其包含在单个源文件中。 实际上,有一种说法是,也许应该将此内容作为内联脚本包括在内,因为它不会在其他页面上使用。 您可能无法将所有内容绝对地合并到一个文件中,但是应用程序发出的HTTP请求数越少,应用程序加载的速度就越快。

缩小JavaScript源文件

良好的编程实践准则规定,编写应用程序代码时应遵循某种风格,并且该准则与其他任何语言一样适用于JavaScript。 问题是,添加到代码中的所有空格,换行符和注释都会增加脚本的文件大小和应用程序的加载时间。

为了减小JavaScript文件的大小,强烈建议您缩小文件的大小,然后再将其放入生产环境。 最小化基本上涉及剥离源代码中的多余部分以生成精简和较小的文件。 举一个潜在的节省示例,在将摘要从155KB减少到70.6KB时,仅缩小就采用了jQuery的最新版本。 这样可以节省将近55%。

当然,建议您缩小源代码是一件好事,但是如何去做呢? 不应期望有理智的人手动缩小代码,因此,值得庆幸的是,这里有一系列开发人员工具可以为您完成工作。 一些受欢迎的工具包括Douglas Crockford的JSMIN,Dean Edwards的Packer以及Dojo工具箱中包含的压缩器。 我个人最喜欢的是Yahoo!®Inc.的YUI Compressor。有关这些工具的更多信息,请参阅参考资料

使用gzip压缩JavaScript源文件

在上一节中,您了解到可以通过缩小JavaScript源文件来大幅度减小它们的大小。 实际上,您可以通过使用gzip压缩文件来进一步减小文件大小。 最好的方法是告诉您的Web服务器gzip JavaScript文件,然后再将它们发送到客户端。 如果您使用的是Apache 2(带有mod_deflate),并且将JavaScript文件存储在同一目录中,则只需在与JavaScript文件相同的位置创建一个.htaccess文件。 该文件的内容应该很简单: SetOutputFilter DEFLATE

压缩JavaScript文件可节省大量资金。 在上一节中,您了解到缩小jQuery源代码会使文件大小从155KB减少到70.6KB(减少55%)。 如果我们将压缩文件gzip压缩,则文件大小将进一步减小到24KB。 在缩小和压缩之间,总共节省了近85%。 结果会因您的代码而异,但是节省的费用可能非常可观。

缓存JavaScript源文件

确保Web浏览器正确缓存您的内容非常重要。 例如,如果您使用Apache(与mod_expires ),并且希望客户端将JavaScript文件缓存两天,则可以将清单11中的指令添加到保留JavaScript文件的位置的.htaccess文件中(假设您有一个专用于JavaScript文件的目录;如果没有,则可能必须在Apache配置文件中执行此操作)。

清单11.将JavaScript文件缓存两天的指令
ExpiresActive on
ExpiresDefault "access plus 2 days"

当然,缓存JavaScript文件的问题在于,如果您进行更改,则具有缓存版本的任何客户端在缓存到期时间内重新访问都会使用脚本的缓存版本,而不是更新版本。 幸运的是,您可以通过将带有版本号的查询字符串附加到加载脚本的<script>标记上,强制客户端检索脚本的最新版本。 该查询字符串不会影响JavaScript代码,但就浏览器而言,它是一个完全独立的文件,它将下载最新版本。 当然,每次更改文件时都必须增加版本号,这一点很重要。 在较大的应用程序中,应使用构建脚本来自动执行此过程,以防止出现问题。

结论

在本文中,您了解了JavaScript和Ajax性能在现代网站和应用程序中的重要性。 首先,您了解了一些好JavaScript编程实践如何对应用程序的响应速度和加载时间产生重大影响。 接下来,您学习了如何使用性能分析和网络分析器工具来评估现有应用程序的性能。 最后,您学习了如何通过采用一些简单但功能强大的技术来减少文件大小和应用程序发出的HTTP请求数量,从而在不修改应用程序源代码的情况下加快现有应用程序的速度。


翻译自: https://www.ibm.com/developerworks/web/library/wa-aj-jsajaxperf/index.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值