一般的做法是什么?
现实世界的场景是,我有几个需要客户端表单验证的HTML页面。为此,我使用一个jQuery插件,将其包含在所有这些页面中。但问题是,我应该:
- 在代码中内联编写配置此脚本的代码片段? - 将所有代码片段包含在一个文件中,该文件在所有这些HTML页面中共享? - 将每个代码片段包含在一个单独的外部文件中,每个HTML页面一个文件?
在这个答案最初发布的时候(2008年),规则很简单:所有脚本都应该是外部的,这样便于维护和提高性能。
(为什么提高性能?因为如果代码是分离的,它可以更容易地被浏览器缓存。)
JavaScript 不属于 HTML 代码,如果它包含特殊字符(例如 <
, >
),甚至会引起问题。
现今,Web 可扩展性已经发生了变化。由于进行多个 HTTP 请求所需的延迟,减少请求次数已经成为一个有效的考虑因素。这使得答案变得更加复杂:在大多数情况下,将 JavaScript 作为外部脚本仍然是推荐的做法。但是对于某些场景,尤其是非常小的代码片段,将它们内联到网站的 HTML 中是有意义的。
可维护性确实是将它们保留为外部文件的原因,但如果配置只有一行(或者通常比获得这些文件所需的HTTP开销更短),则在性能方面最好将其内联。始终要记住,每个HTTP请求都会产生一定的执行时间和流量开销。
自然而然地,当你的代码长度超过几行,并且不仅仅针对一个页面时,所有这些都变得无关紧要。当你想要重用该代码时,将其制作为外部文件。如果不这样做,请查看其大小,然后再考虑是否需要将其制作为外部文件。
如果你只关心性能,那么这个帖子中的大部分建议都是完全错误的,在 SPA 时代变得越来越错误,因为我们可以认为没有 JS 代码页面是无用的。我花费了无数小时优化 SPA 页面加载时间,并使用不同的浏览器验证了这些结果。通过重新编排您的 HTML,性能提升可能会非常显著。
要获得最佳性能,您必须将页面视为两级火箭。这两个阶段大致对应于 <head>
和 <body>
阶段,但您应该将它们视为 <static>
和 <dynamic>
。静态部分基本上是一个字符串常量,您应该尽可能快地将其推到响应管道中。如果您使用了许多设置 cookie 的中间件(这些需要在发送 HTTP 内容之前设置),这可能有点棘手,但原则上只需刷新响应缓冲区,希望在服务器上跳转到某些模板代码(razor、php 等)之前。这可能听起来很困难,但我解释错了,因为它实际上相当简单。正如您可能已经猜到的那样,这个静态部分应该包含所有 JavaScript 内联和压缩。它可能看起来像这样:
<!DOCTYPE html>
<html>
<head>
<script>/*...inlined jquery, angular, your code*/</script>
<style>/* ditto css */</style>
</head>
<body>
<!-- inline all your templates, if applicable -->
<script type='template-mime' id='1'></script>
<script type='template-mime' id='2'></script>
<script type='template-mime' id='3'></script>
由于发送这部分内容几乎不花费任何成本,所以您可以预期客户端将在连接到服务器后大约5ms +延迟左右开始接收此部分内容。假定服务器距离合理,这种延迟可能在20毫秒到60毫秒之间。浏览器将在获得此部分内容后立即开始处理,处理时间通常会比传输时间支配因子要多20倍或更多,这现在是您用于<dynamic>
部分的服务端处理的平均分摊时间。
浏览器(chrome,其他浏览器可能慢20%)处理inline jquery + signalr + angular + ng animate + ng touch + ng routes + lodash大约需要50ms。这本身非常惊人。大多数Web应用程序的代码量都比这些流行库加在一起少,但是假设您拥有同样多的代码,那么我们将在客户端赢得延迟+100ms的处理(这种延迟胜利来自第二个转移块)。当第二个块到达时,我们已经处理了所有js代码和模板,可以开始执行dom变换。
您可能会认为这种方法与内联概念无关,但事实并非如此。如果您不是内联,而是链接到CDN或您自己的服务器,则浏览器必须打开另一个连接并延迟执行。由于这种执行基本上是免费的(因为服务器端正在与数据库通信),因此必须清楚地了解所有这些跳跃的成本都比根本不进行任何跳跃更高。如果存在浏览器怪癖,说外部js执行得更快,我们可以测量哪个因素占主导地位。我的测量结果表明,在此阶段,额外的请求会影响性能。
我经常在优化SPA应用程序方面工作。人们通常认为数据量很重要,但实际上是延迟和执行经常占主导地位。我列出的压缩库总共添加了300kb的数据,而仅68 kb经过gzip压缩,或者在2mbit 3g / 4g手机上下载需要200ms,这恰好是在同一手机上检查它是否已经在其缓存中具有相同数据的延迟,即使它被代理缓存,因为移动延迟税(手机到塔延迟)仍然适用。与此同时,首跳延迟较低的桌面连接通常拥有更高的带宽。
简而言之,现在(2014年),最好内联所有脚本,样式和模板。
编辑(2016年5月)
随着JS应用程序的不断增长,我的一些有效负载现在堆叠到3+兆字节的压缩代码,显然,至少常见库不应再内联。
将JavaScript外部化是雅虎性能规则之一: http://developer.yahoo.com/performance/rules.html#external
虽然硬性规定总是将脚本外部化通常是一个好选择,但在某些情况下,您可能希望内联一些脚本和样式表。但是,您应该只内联那些您知道会提高性能的内容(因为您已经进行了测量)。
我能为内联JavaScript提供的唯一防御是,使用带有.NET MVC的强类型视图时,您可以在JavaScript中引用C#变量,这对我非常有用。
三个考虑因素: