规则9 - 减少DNS查找
Internet
是通过IP
地址来查找服务器的,由于IP
地址很难记忆,通常用包含主机名的URL
来取代它,但当浏览器发送其请求时,IP
地址是必须的,这就是Domain Name System
(DNS
)所处的角色。DNS
将主机名映射到IP
地址上,就将电话本将人名映射到他们的电话号码一样,当你在浏览器中输入www.baidu.com
的时候,连接到浏览器的DNS
解析器会返回服务器的IP地址。
如果一个服务器被另外一个具有不同IP
地址的服务替代了,DNS
允许用户使用同样的主机名来连接到新的服务器。(主机名相当于姓名,这种情况下相当于对应的电话号码更新了),或者可以将多个IP地址关联到一个主机名,为网站提高冗余度。(相当于存储了一个人的多个电话号码)
当然,DNS
也是开销,通常浏览器查找一个给定的主机名的IP地址需要花费20-120毫秒,在DNS
查找完成之前,浏览器不能从主机名那里下载到任何东西。响应时间依赖于DNS
解析器、它所承担的压力、你与它之间的距离和你的带宽速度。
DNS缓存
DNS
查找可以被缓存起来以提高性能,这种缓存可以发生你的ISP
(Internet Service Provider
互联网服务提供商)或局域网中一台特殊的缓存服务器上,但我们这里要探索的是发生在独立用户计算机上的DNS
缓存,在用户请求了一个主机名之后,DNS
信息会留在操作系统的DNS
缓存中,之后对于该主机名的请求将无需进行过多的DNS
查找,至少短时间内不需要。
很多浏览器拥有其自己的缓存,和操作系统的缓存相分离。只要浏览器在其缓存中保留了DNS
记录,它就不会麻烦操作系统请求这个记录。只有当浏览器缓存丢弃了记录时,它才会向操作系统询问地址——然后操作系统或者通过其缓存来响应这个请求,或许将请求发送给一台远程服务器,这时就会发生潜在的速度降低。而且浏览器对缓存DDS
记录的数量也有限制,而不管缓存记录的时间。如果用户在短时间内访问了很多具有不同域名的网站,较早的DDS
记录将被丢弃,必须重新查找该域名,虽然可能操作系统缓存还保留着这个记录,但是也比无需查找要慢。
TTL
服务器可以表明记录可以被缓存多久,查找返回的DDS
记录包含了一个存活时间值(TTL
, Time-to-live
),该值告诉客户端可以对该记录缓存多久。但是只有操作系统缓存会考虑TTL值,浏览器通常忽略该值,并设置它自己的事件限制。此外,HTTP协议中的Keep-Alive特性可以同时覆盖TTL和浏览器的时间限制,换句说,只要浏览器和web服务器愉快的通信着,并保持着TCP连接打开的状态,就没有理由进行DNS查找。
客户端收到的DNS
记录的TTL值平均只有最大TTL
值的一半,因为DNS
解析器自身也拥有与DNS
记录相关的TTL
,当浏览器进行DNS
查找时,DNS
解析器返回的时间是其记录的TTL
的剩余时间。如果最大TTL
是300秒,DNS
解析器可能返回的时间是1~300秒,平均150秒,对于给定的主机名,每次执行DNS
查找时接受的TTL
值都会变化。
window操作系统上的DNS缓存
window操作系统上的DNS
缓存由DNS Client
服务进行管理。你可以使用ipconfig
命令来查看和刷新DNS Client
服务:
ipconfig / displaydns
ipconfig / flushdns
重新启动也可以清空DNS Client
服务缓存。重新启动浏览器会清空浏览器缓存,但不会清空DNS Client
服务缓存。
IE上的DNS缓存
IE
的DNS
缓存由三个注册表设置控制——DnsCacheTimeout
、KeepAliveTimeout
、ServerInfoTimeout
,可以创建在下面的注册表键下:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\InternetSettings\
IE中这三个设置的默认值是:
-
DnsCacheTimeout
: 30分钟-
IE
一旦缓存了DNS
记录就会使用30分钟作为TTL
值,如果服务器返回了一个小于30分钟的TTL
值,在IE
中不会增加DNS
查找的数量。
-
-
KeepAliveTimeout
: 1分钟- 默认设置下,一个
TCP
连接将会一直使用,直到其被空闲1分钟为止,由于连接是持久的,所有无需DNS
查找——Keep-Alive
通过重用现有连接避免了重复的DNS
查找。
- 默认设置下,一个
-
ServerInfoTimeout
: 2分钟- 该设置之后,即使如果没有
Keep-Alive
,如果一个主机名每2分钟重用了一次(被请求过),并且没有发生错误,也无需进行DNS
查找(即使超过30分钟也可以)。
- 该设置之后,即使如果没有
如果网络中心通过DNS
来转移走流量,此时如果该IP
还在运行,那么IE
用户至少要等待30分钟才能更新DNS
,而且IE
的活跃用户(每两分钟访问一次)将会一直使用旧的IP
地址而不会更新DNS
直到发生错误。
FireFox
它具有如下的相关配置:
-
network.dnsCacheExpiration
—— 1分钟- FF浏览器DNS缓存的记录比其TTL多1分钟,如果服务器返回的TTL值很小,很可能增加在FF中访问页面中的DNS查找次数。
-
network.dnsCacheEntries
—— 20- 设置默认只有20条DNS记录能被缓,这意味着访问很多不同域名网站的用户访问时将会产生更多的DNS查找次数。
-
network.http.keep-alive.timeout
—— 5分钟- 持久连接将一直保持,直到被空闲5分钟为止。
当客户端DNS
缓存为空时,DNS
查找的数量与web
页面中的主机名的数量相等,这包括页面的URL
、图片、脚本文件、样式表、Flash等的主机名,所以,减少主机名的数量就可以减少DNS
查找的数量。
所以在将并行下载的时候说过,主机名不是越多越好,因为主机名越多,DNS
查找的次数越多,但减少主机名潜在地减少了页面中并行下载的数量,所以在主机名和并行下载数量的设置上要有一个合适的取舍。
小结
1、为什么会有DNS
查找?因为IP
地址很难记忆,使用DNS
可以将主机名映射到IP地址上,而且还可以关联多个IP
地址。
2、使用DNS
查找会发生什么?浏览器查找一个给定的主机名的IP地址需要花费20-120毫秒,在DNS
查找完成之前,浏览器不能从主机名那里下载到任何东西。
3、DNS
的缓存机制?在用户请求了一个主机名后,DNS
记录会缓存在操作系统中,当然浏览器也拥有其自身的DNS
记录缓存,只有当浏览器自身的DNS
记录没有的时候,才会询问操作系统。如果此时操作系统也没有,就会发送请求到另一个远程服务器中获取新的DNS
记录。
4、如何优化DNS
查找次数?
+ 使用较少的域名(主机名)来减少DNS
查找次数:因为每个域名都对应一个IP
地址,都需要进行DNS
查找。
+ 使用keep-alive
来重用现有连接,减少DNS
查找次数:因为浏览器和服务器保持着连接,则很明显的无需进行DNS
查找。(我们已经同上电话了,就无需再去电话簿上找你的电话),浏览器和服务器通过Connection
头来指出对keep-alive
的支持,保持持久连接。
规则10 - 精简JS
精简是指从代码中移除不必要的字符以减小其大小,改善响应时间,进而改善页面加载时间。
混淆是可以应用在源代码上的另外一种方式,和精简一样,它也会移除空白和注释,同时它还会改写代码,作为改写的一部分,函数和变量名将被替换成更短的字符,这时的代码将更加精炼,也更难阅读,通常这样做是为了增加对代码进行反向工程的难度,同时也提高了性能,而且这比精简代码更能减小代码的大小。但是混淆也有缺点,混淆本身比较复杂,过程容易出错,混淆之后,代码在产品环境中更难阅读,更难调试(当然现在都可以使用构建工具来解决这两点)。
压缩和精简
gzip
压缩对组件大小产生的影响最大,但精简能够进一步减小文件大小,随着JS
的使用量和大小的不断增长,精简JS
代码能够带来更多的节省。
精简CSS
精简CSS
能够带来的收益通常要小于精简JS
,因为CSS
文件中的注释和空白通常比JS
少,最大的节省通常来自于优化CSS
——合并相同的类,移除不使用的等。不能够使用变量来代替,并且CSS
依赖顺序的本质(层叠样式表)决定了想从这方面精简CSS
这将是一个复杂的过程。
规则11 - 避免重定向
重定向(Redirect
)用于将用户从一个URL
路由到另一个URL
,重定向有很多种——301和302是最常用的两种,通常针对HTML
文档进行重定向,但也可能用在请求页面中的组件,使用重定向会有很多不同的原因,比如网站重新设计、跟踪流量、记录广告点击和建立易于记忆的URL
,当然重定向会使你的页面变慢。
在web
服务器向浏览器返回一个重定向时,响应中就会拥有一个3xx的状态码。这表示用户代理必须执行进一步的操作才能完成请求。
-
300 Multiple Choices
基于Content-Type 多种选择 -
301 Moved Permancently
永久移动 -
302 Moved Temporarity
临时移动 -
303 See Other
对302的说明,查看其它位置 -
304 Not Modified
未修改 -
305 Use Proxy
使用代理 -
307 Temporary Redirect
临时重定向 对302的说明
301和302是使用最多的,303和307是HTTP 1.1
规范中添加的用来澄清对302的使用,但是几乎没人使用。
HTTP 1.1 301 Moved Permancently
Location: http://test.com/newurl
Content-Type: text/html
浏览器会自动将用户带到Location
字段所给出的URL
,重定向所必需的所有信息都处在在这个响应头中了,301和302在实际中都不会被缓存,除非有附加的头要求它这么做,比如Expires
和Cache-Control
。
还有其他方法可自动将用户重定向其他URL
,HTML
文档中的包含 meta refresh
标签可以在其content
属性指定的秒数后重定向用户
JS
也可用于执行重定向,将document.location
设置为期望的URL
即可,如果你必须进行重定向,最好的技术是使用标准的 3xx HTTP
状态码,这主要是为了确保后退按钮能够正常工作。
重定向是如何影响性能的,如果第一个HTTP请求是重定向,它会延迟整个HTML
文档的下载,在HTML
文档到达之前页面中不会呈现任何东西,也没有任何组件会被下载,延迟了用户期望的反馈。
什么时候会使用重定向?有没有优化方案?
1、缺少结尾的斜线
有一种重定向最为浪费、发生得也很频繁,但web
开发人员通常没有意识到它,它发生在URL的结尾必须出现斜线/时而没有出现时。缺少结尾的斜线时发生重定向有着很充分的理由,它允许自动索引,自动转到默认的index.html
上。当主机名缺少结尾斜线时不会发生重定向,因为浏览器在进行GET
请求时必须指定路径,如果没有,它就简单地使用文档目录(/
)。
2、连接网站
如果网站的后端被重写,新的实现中的URL
很可能会有所不同,将用户从旧的URL
转移到新的URL
最简单的方式就是使用重定向,将旧网站连接到新网站只是重定向常见表现形式之一,其他形式还包括将一个网站的不同部分连接起来,以及基于一些条件(浏览器类型,用户账户类型等)来引导用户,使用重定向来连接两个网站很简单且只需要很少的代码。虽然重定向降低了开发的复杂性,但是损害了用户体验,所以可以用更多的开发工作来优化后端代码以减少这一部分的重定向。
3、跟踪内部流量
重定向经常用于跟踪用户流量的流向,例如,主页的nav
导航到另一个功能页面,点击nav
的时候,将用户先导航到专门记录web
日志的页面,然后这个页面返回一个301响应,并且Location
指向真正的功能页面,这时候就可以通过web
日志得知用户在离开主页是去往了哪个功能页面。
优化选择是使用referer
日志来追踪流量去向,当用户从页面导航到功能页面时,会携带一个referer
值(表示来源页面的URL
)去访问功能页面,这样就避免了向用户发送重定向,也就改善了响应时间,使用该方法的难处在于,要向对离开主页的流量进行统计,就必须分析所有功能页的日志。对于内部流量——同一家公司各个网站之间的浏览,值得通过建立referer
日志来避免重定向,以此来节省时间。
4、跟踪出站流量
当你尝试跟踪用户流量时,你会发现链接可能将用户带离你的网站,在这种情况下,使用referer
的方式就不太现实了,比如,使用搜索引擎搜索时,可以将每个搜索结果链接包装到一个重定向中来解决跟踪的问题,重定向一个专用的记录页面并将搜索结果链接带过去,然后在记录页面中返回302响应,将Location
设置为搜索结果的链接。这样就能通过分析这个专用的记录页面来分析用户离开你的网站去了哪。
当然为了避免重定向,也有别的方法,使用信标当用户点击搜索结果链接时都要发送一个信标(http
请求),信标响应通常是一个1px * 1px的透明图片,不过返回204更好,因为它更小,这通过为每个链接绑定onclick
事件处理器来完成。这种该方法可能和使用重定向一样慢,因为这两种技巧都必须有一个额外的http
请求。在跟踪弹出式广告的点击量,这种方式就很好。因为弹出式广告不会卸载当前页面,都是使用a标签的target="_blank"
属性来打开新页面,这样图标请求就会顺利完成而不会被中断。
另一种方式使用 XMLHTTPRequest
来发送信标,但在卸载页面之前只需要等到请求到达readyState 2
(已发送)即可,无需等待响应,这比等待重定向的整个HTTP
响应要快,也比一个完整的信标HTTP
请求要快,但你必须决定是否有必要采用这么复杂的方式。
5、美化URL
使用重定向用来支持美观且易于记忆的URL
。因为有的功能页面的html
文件嵌套过深,或者说文件夹名过长,直接访问这个接口URL
不太适合产品,所以用简单易记的URL
去重定向到真正的URL
用来优化用户使用。
小结
1、在重定向完成之前,真正想访问的那个页面的所有组件都不会开始下载,延迟了用户的期望反馈。
2、在需要使用重定向时,都可以寻找别的方案来替换,所以在开发工程中要尽量的避免使用重定向的方法来实现需求。
规则12 - 删除重复脚本
导致重复脚本的重复有两个因素——团队大小和脚本数量。由于来之不同团队的很多人共同开发,很容易有相同的脚本被重复添加,例如,两个贡献JS的开发者可能都需要管理Cookies
,因此他们都添加了公司的cookie.js
脚本,两个开发者都不知道对方已经向页面中添加了这个脚本。
重复脚本损伤性能的方式有两种——不必要的HTTP
请求和执行JS
所浪费的时间,不必要的HTTP
请求只会发生在IE
中,而不会发生在FF
中,在IE
中,如果脚本被包含两次且没有缓存,浏览器会在页面加载期间产生两个HTTP
请求,即使脚本可以缓存,加载一次该页面可以填充缓存,用户刷新页面时,还是产生两个HTTP
请求,尤其是产生了两个条件GET
请求。
除了会产生不必要的HTTP
请求,对脚本进行多次求值也会浪费时间,对JS
进行多余的执行在IE
和FF
中都存在,与脚本是否缓存无关。
总体来说:
- 在页面中多次包含相同的脚本会使页面变慢
- 在
IE
中,如果脚本没有被缓存,或在重新加载页面时,会产生额外的HTTP
请求。 - 在
FF
和IE
中,脚本会被多次求值。 - 所以请确保脚本只被包含一次。
规则13 - 配置或移除ETag
减少呈现页面时所必需的HTTP
请求数量是加速用户体验的最佳方式,可以通过最大化浏览器缓存组件的能力来实现这一目标,但当网站被宿主在多余一台服务器上时,ETag
头可能会阻碍缓存。
ETag
(Entity Tag
实体标签)是web
服务器和浏览器用于确认缓存有效性的一种机制,浏览器下载组件时,会将它们存储到缓存中,在后续的页面浏览中,如果缓存的组件是”新鲜的“,浏览器就会从磁盘上读取它,避免产生HTTP
请求,如果组件没有过期,那它就是新鲜的,这取决于Expires
的值。
Expires: Thu, 15 Apr 2010 20:00:00 GMT
HTTP
规范建议服务器不应将Expires
日期设置为未来一年之后,但这是一个指导原则,浏览器支持超过未来一年的Expires
过期日期,对于不太会过期的组件(比如网站的logo
),将过期日期设置的久远一些能够更有效的避免HTTP
请求。
如果缓存的组件过期了(或者用户明确的重新加载了页面),浏览器在重用它之前必须首先检查它是否仍然有效,这称做条件GET
请求(使用Expires
头能避免HTTP
请求),虽然这个HTTP
请求也需要消耗时间,但是这比简单地下载已过期的组件效率要高,如果浏览器缓存中的组件是有效的,原始服务器不会返回整个组件,而是返回一个304
状态码。服务器在检测缓存的组件是否和原始服务器上的组件匹配时有两种方式:
- 比较最新的修改日期
Last-Modified
和If-Modified-Since
- 比较实体标签
ETag
和If-None-Match
比较修改日期
// 第一次请求
// 请求头
GET /logo.gif HTTP 1.1
Host: us.test.com
// 响应头
HTTP 1.1 200 OK
Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT
Content-Length: 1195
// ========================
// 第二次请求
// 请求头
GET /logo.gif HTTP 1.1
Host: us.test.com
If-Modified-Since: Tue, 12 Dec 2006 03:03:59 GMT
// 响应头
HTTP 1.1 304 Not Modified
比较实体标签
ETag
提供了另外一种方式,用于检测浏览器缓存中的组件与原始服务器上的组件是否匹配,(”实体“是我们之前提到的”组件“的另一种称呼),在HTTP 1.1
中引入,是唯一表示了一个组件的一个特定版本的字符串,唯一的格式约束是该字符串必须用引号引起来。
// 第一次请求
// 请求头
GET /logo.gif HTTP 1.1
Host: us.test.com
// 响应头
HTTP 1.1 200 OK
Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT
Content-Length: 1195
ETag: "10c24bc-4ab-457e1c1f"
// ========================
// 第二次请求
// 请求头
GET /logo.gif HTTP 1.1
Host: us.test.com
If-Modified-Since: Tue, 12 Dec 2006 03:03:59 GMT
If-None-Match: "10c24bc-4ab-457e1c1f"
// 响应头
HTTP 1.1 304 Not Modified
ETag
的加入为验证实体提供了比验证最新修改日期更为灵活的机制,例如,如果实体依据User-Agent
或Accept-Language
头而改变,实体的状态可以反映在ETag
中,此后如果浏览器必须验证一个组件,它会使用If-None-Match
头将ETag
值传回原始服务器,如果匹配则返回304
状态码,使响应较小。
优势
- 有些
URL
是多语言的网页,相同的URL
会返回不同的东西。还有不同的Session
有不同的Cookie
也就有不同的内容。这种情况下如果使用Proxy
,Proxy
就无法区分导致串门,只能简单的取消cache
功能。Etag解决了这个问题,因为它能区分相同URL
不同的对象。Etag
在不同URL
之间没有可比性,也就是不同URL
相同Etag
没有特别意义。 - 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新
GET
; - 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),
If-Modified-Since
能检查到的粒度是秒级的,这种修改无法判断(或者说UNIX
记录MTIME
只能精确到秒) - 某些服务器不能精确的得到文件的最后修改时间;
缺陷
ETag
字符串的生成,通常与组件的某些属性有关,这些属性对于特定的、寄宿了网站的服务器来说是唯一的,而当浏览器从一台服务器获取了原始组件之后,又向另外一台不同的服务器发起条件GET
请求,ETag
是不会匹配的,这对于使用服务器集群来处理请求的网站来说,这是一种很常见的情况,对于拥有多台服务器的网站,Apache
和IIS
向ETag
中嵌入的数据都会大大地降低有效性验证的成功率。
Apache 1.3
和2.x
的ETag
格式是inode-size-timestamp
,文件系统使用inode
来存储文件类型、所有者、组和访问模式等信息,尽管在多台服务器上一个给定的文件可能位于相同的目录、具有相同的大小、权限、时间戳等,从一台服务器到另一台服务器,其inode
仍然是不同的。IIS 5.0
和6.0
上的ETag
有着类似的问题,IIS
上的ETag
的格式是Filetimestamp:ChangeNumber
,ChangeNumber
适用于跟踪IIS
配置变化的计数器,对于一个网站背后的所有IIS
服务器来说,ChangeNumber
不大可能相同。
最后的结果是,对于完全相同的组件,从一台服务器到另一台,Apache
和IIS
产生的ETag
是不会匹配的。如果不匹配,则不会返回304
状态码,而是返回一个普通的200
响应及组件的相关数据,如果你在一台服务器上寄宿网站,这不是问题,但你如果使用了服务器集群,则组件的下载次数可能会比必须进行下载的次数多得多,这将导致性能的下降,如果你拥有10台服务器,则用户只有10%的概率在第二次访问到相同的服务器,得到一个正确的304响应,其余90%的概率将会得到无用的200响应并下载所有的数据。
ETag
还降低了代理缓存的效率,用户缓存的ETag
经常和代理缓存的ETag
不匹配,这导致不必要的请求被发送到原始器,这种情况下,用户和代理之间就不会产生304响应,而是会产生两个又慢又大的200响应,(原始服务器到代理的 和 代理到用户的),同时ETag
的默认格式生成字符串可能还会引入安全问题。
If-None-Match
比If-Modified-Since
具有更高的优先级,你可能希望如果ETag
不匹配但最新修改日期是相同的也能发送一个304
响应,但实际并不是这样的,根据HTTP 1.1
规范,如果请求中同时出现了这两个头,则原始服务器禁止返回304除非请求中的条件头字段全部一致。
ETag用还是不用
如果你在多台服务器上寄宿你的网站,而且你使用的是具有默认配ETag
配置的Apache
和IIS
,你的用户将面对缓慢的页面,你的服务器会具有很高的负载,你会消耗大量的带宽,而且代理也不能有效的缓存你的内容,即使你为组件添加了长久的Expires
头,一旦用户单击了Reload
或Refresh
按钮,依然会产生条件GET
请求,对于ETag
带来的问题你没有办法绕过它。
如果你的组件必须通过最新修改日期之外的一些东西来进行验证,则ETag
是一种强大的方法,反之,你最好将其移除,Apache
和IIS
都将ETag
视为一个性能问题,并建议修改ETag
的内容,Apache 1.3.23
版本之后都支持FileETag none
的指令来移除ETag
中的inode
值,只留下size
和timestamp
作为组件的ETag
,类似的IIS
也可以为所有服务器设置相同的ChangeNumber
,保留文件的时间戳作为ETag
中仅有的另一块信息。遵从这一建议后,ETag
中将只包含size
和timestamp
(Apache
)或只有timestamp
(IIS
),这样,会有很多组件拥有相同的信息,所以最好还是将ETag
完全移除,Last-Modified
头可以提供完全等价的信息,而且移除ETag
可以减少响应的时间和和后续请求的HTTP
头大小。
小结
1、ETag
被用来在服务器和浏览器之间确认组件缓存的有效性,使用ETag
响应头和If-None-Match
请求头来进行匹配。
2、If-None-Match
比If-Modified-Since
优先级更高,如果请求中同时出现了这两个头,则原始服务器禁止返回304除非请求中的条件头字段全部一致。
3、优势:有些URL
是多语言的网页,相同的URL
会返回不同的东西,ETag
能够区分相同的URL
不同的对象;如果组件仅仅变化了修改时间,而没有修改内容,ETag
也可以有效匹配来减少响应;而且If-Modified-Since
能检查到的粒度是秒级的,如果1秒钟修改了N次这种修改将无法判断,ETag
可以避免这个问题;有的服务器并不能返回准确的最新修改时间。
4、在使用服务器集群时,相同的组件每个服务器生成的ETag
都不相同,这大大的增加了不必要HTTP
请求的次数。而且默认的生成ETag的格式可能还会引发安全问题。
5、所以,如果必须使用ETag
则请配置它,在Apach
e中移除ETag
中inode
的值,在IIS
中为所有服务器设置相同的ChangeNumber
,但是最好建议你在不是必须使用时完全移除它。
规则 14 - 使Ajax可缓存
Web 2.0 、 DHTML 、Ajax
web2.0
包含很多概念,其中之一就是DHTML
,Ajax
是DHTML
中的一项关键技术。web2.0
的关键概念包括类似应用程序的用户界面(UI
)和来自多个Web Service
的聚合信息(数据)。web
页面变得越来越像一个具有良好定义的输入、输出的应用程序。DHTML
和Ajax
是实现这些概念的技术。
DHTML
(动态HTML)允许在页面加载完毕后,HTML
页面的表现能够变化(与用户交互),这使用JS
与浏览器的文档对象DOM
进行交互来实现。Ajax
是DHTML
中使用的一项技术,客户端可以获取和显示用户请求的新信息而无需重新加载页面。
Ajax
表示异步的JavaScript
和 XML
(Asynchronous JavaScript and XML
),它不是一个单独的、需要许可证的技术,而是一组技术。包含JS
、CSS
、DOM
和异步数据获取,Ajax
的目的是为了突破web
本质的开始-停止的交互方式,向用户显示一个白屏然后重绘整个页面不是一种好的用户体验,而Ajax
在UI
和Web
服务器之间插入了一层,这个Ajax层位于客户端,与web服务器进行交互以获取请求的信息,并与表现层交互,仅更新那些必要的组件,它将web体验从”浏览页面“转变为”与应用程序进行交互“。
异步与即时
Ajax
的一个明显优点就是向用户提供了即时反馈,因为它异步的从后端web
服务器请求信息,但是使用Ajax
,并不能保证用户能够”即时”的收到反馈,用户是否需要等待响应和页面更新取决于你如何使用Ajax
,其中关键因素在于Ajax
请求是被动请求还是主动请求,被动请求是为了将来使用而预先发起的,例如在一个基于web
的Email
客户端中,可能会使用被动请求在用户真正需要之前下载用户的地址簿,经过被动加载,当用户需要为一个Email
消息添加地址时,客户端能够确保地址簿已经存在于缓存中。主动请求是基于用户当前的操作而发起的,例如查找所有与用户搜索条件相匹配的Email
消息。
即便Ajax
请求是异步的,用户可能仍需等待响应,不过用户不必去忍受整个页面的重新加载了,而在用户等待时,UI
仍然是可以响应的,比如Loading
。当然在用户在进行进一步操作之前仍可能需要坐在那里等待搜索结果显示出来,所以”异步“并不代表”即时“。
优化Ajax请求
当发起主动Ajax
请求时,用户仍需等待,要改善性能,优化Ajax
请求很重要,优化主动Ajax
请求的技术同样适用于优化被动Ajax
请求,但是主动请求对用户体验影响更大。改善这些主动Ajax
请求最重要的方式就是使响应可缓存,就像规则3中的讨论,同样还有其他规则也适用于Ajax
请求:
- 规则4 - 压缩组件
- 规则9 - 减少
DNS
查找 - 规则10 - 精简
JS
- 规则11 - 避免重定向
- 规则13 - 配置或移除
ETag
然而规则3-使用缓存是最重要的。
现实世界中的Ajax缓存
例子:在启用一个Email
的客户端时,可以被动请求下载前三个Email
消息的正文,用户很有可能点击这些Email
消息中的一个或几个,它们此时已经被下载到客户端,这意味着用户无需任何Ajax
就能看到他的Email
消息,当用户想查看一个不在前三个消息之列的Email
消息,就会产生一个主动的Ajax
请求,用户在阅读消息之前必须等待响应。之后假设用户离开了Email
客户端并浏览了其他网站,然后返回并再次点击第4个Email
信息,不出所料,会又一次发送完全相同的请求,所以可以使用一个长久的Expires
头,来将响应缓存在磁盘中,优化用户体验,当然这对于开发者来说是违反只觉得,因为这应该是一个动态生成的响应,只包含与这世界上一个用户相关的信息,使浏览器缓存这些数据看上去没有任何意义,但是一个用户可能每天或者每周进行很多次对该Email
消息的请求,如果你可以为他缓存响应,就会看到缓慢的和快速的用户体验之间的差距,使这些Ajax
请求可缓存,除了改变HTTP
头之外还需要进行更多的操作,响应的个性化和动态本质必须被反应到缓存中,可供采用的方式就是使用查询字符串,保证这个响应只对当前用户有效,可以将用户名放到查询字符串中来做到这一点。
响应可能因为数据隐私的原因不能被缓存。当数据认为是私有的时,大多会使用Cache-Control: no-store
头,使用这个头之后,响应根本不会被写入到磁盘中,但是HTTP
规范警告说不要依赖这一机制来确保数据的隐私性,恶意的或危险的缓存会完全忽略Cache-Control: no-store
头。处理数据隐私性的另外一种方式就是使用安全通信协议如安全套接字层(Secure Sockets Layer, SSL
)。
小结
1、Ajax
是使用户能够无需等待整个页面的重新加载就可以与页面进行交互的技术。提高了用户的体验,所以优化Ajax请求十分重要。
2、优化Ajax
请求的方式,最重要的就是时响应具有长久的Expires
头,当然还有之前所提到的压缩组件、减少DNS
查找、精简JS
、避免重定向、配置或移除ETag
。