// 最近在看这本书,顺便做下笔记。 // Rule 3: Add an Expires Header // ------------------------- 使用 Expires HTTP Header 使浏览器或代理服务器缓存文件,减少 HTTP Request 消耗的时间 ---- Expires: Thu,19 Dec 2010 20:00:00 GMT -- Cache-Control 除了 Expires 之外,还有一个相关的可选的 HTTP Header: Cache-Control, Cache-Control 是在 HTTP/1.1 中增加的,是为了减少 Expires header 的限制增加的;因为 Expires 指定一个特殊的时间,他要求 server 和 client 之间的时间要同步。 Cache-Control 使用 max-age 来指定内容缓存的时间,以秒定义. Cache-Control:max-age=315360000 同时使用 Expires 和 Cache-Control:max-age 时,max-age 会覆盖 Expires header;最好同时使用 Expires 和 Cache-Control,因为部分浏览器不支持 HTTP/1.1 -- ---- ---- mod_expires mod_expires 是 Apache 的 module,用来设定 Expires 和 Cache-Control header mod_expires 默认已经安装,只不过没有启用,可以在 /etc/apache2/mods-available 找到,启用该模块使用命令: sudo a2enmod expires // 然后重启 Apache 就可以了 当然这样不会自动给文件加 Header,需要一些命令,这些命令可以写在 .htaccess 文件了。具体命令见: http://httpd.apache.org/docs/2.2/mod/mod_expires.html ------- expires 设置:(最好在 .htaccess 文件中设置) -- ExpiresActive On <FilesMatch "/.(png|gif|jpg|png|js|css)$"> ExpiresDefault "access plus 1 month" </FilesMatch> -- ------- ------------------------- // Rule 4: Gzip Compononts // Reduce the size of the HTTP response 从 HTTP/1.1 开始支持 Accept-Encoding:gzip,deflate 头。 Gzip 是目前使用最普遍的了,也是最高效的压缩算法。 IE6+,Mozilla 5.0+ 支持 gzip 一般来说支持 deflate 的浏览器会支持 gzip,反过来却不一定。 ------- 1, html,xml,json,js,css 可以使用 gzip 压缩 2, 图片和 pdf 不能用 gzip 压缩,因为他们已经被压缩了,尝试压缩可能会增加大小。 3, 压缩的时候会消耗额外的 cpu 周期, browser 也需要时间来解压。 4, 一般来说大于 1K 或 2K 的文件值得压缩 5, Gzip 压缩后大约减小 66%, deflate 压缩后大约减小 60% ---- Apache1.3 使用 mod_gzip 模块来完成压缩 Apache2.x 使用 mod_deflate 模块来完成压缩 ---- mod_deflate 的设置 -- AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/x-javascript application/javascript // 其他设置见:http://httpd.apache.org/docs/2.0/mod/mod_deflate.html -- 1, 服务器是否压缩取决于 Request 的 Accept-Encoding 头信息 2, 避免代理服务器在第一次请求 Gzip 的文件并缓存后对不支持 Gzip 的浏览器发送 gzip 内容产生的错误, 在 Response 中使用 Vary Header 来解决这个问题 Vary:Accept-Encoding 服务器告诉代理服务器根据 Accept-Encoding 来选择缓存的正确的 response 3, Vary:Accept-Encoding,User-Agent 使代理服务器根据 Accept-Encoding 和 User-Agent 来选这正确的缓存 response 返回 4, 使代理服务器不缓存 response 的方法: Vary: * 或 Cache-Control:private。后者更受欢迎(Google 和 Yahoo! 都使用) (使代理服务器不缓存所有的请求) // Rule 5: Puts stylesheets in the document HEAD using the LINK tag // 说明下原因吧: 1, 如果将 stylesheets 放在 html document 的下面,浏览器一般来说是按照component 在 html document 出现的顺序来下载的,因此在底部的 stylesheets 会最后被下载。Internet Explorer为了避免显示过程中发现有新的 stylesheets ,需要从新显示 html,会等到最后的底部的 stylesheets 下载完后才显示 html,这会造成一段时间的浏览器空白,对用户体验不好(即使这个 stylesheets 不会用于显示,IE 也会等到他下载完才显示 html)。 2, 上面所说的情况在 Firefox 中不会出现,Firefox 不等底部的 stylesheets 下载下来就会显示已经下载的内容;因此如果将 stylesheets 放在底部的话会导致这个 stylesheets 下载完后浏览器发现样式变了,会改变 html 的样子。 3, 除了用 <link> 加载外部 stylesheets 外,也可以用 import(但是有问题) <mce:style><!-- @import url( main.css); --></mce:style><style mce_bogus="1"> @import url( main.css);</style> // 说明: @import 必须在所有其他前面 这种情况也会导致空白,因为这时候这个 css 也是最后才加载的。(可能是浏览器下载完 html 解析需要一段时间,也就是这段时间使 这个 import 的 css 被最后加载了) // Rule 6: Move scripts to the bottom of the page // 原因:scripts 会阻止平行 http 下载,在下载 scripts 时,浏览器不会发送其他 http 请求; 如果 scripts 放在 html 的头部,在 scripts 没有加载完之前,浏览器不请求其他内容, 这时候会导致浏览器空白。(和在 ie 中将 stylesheets 放在底部一样) // 在 HTTP/1.1 规范中有一条建议:浏览器同时下载两个内容在同一个主机上。 (Browsers download two components in parallel per hostname.) 因此如果一个网页将它的内容放在两个主机上,全部的请求时间会减半。 每个主机上平行下载两个内容是一个 guideline;IE 和 Firefox 可以修改这个值。 IE 中是在 注册表中修改这个值, Firefox 中修改 about:config 里面的 network.http.max-persistent-connection-per-server //-- 可以使用 CNAMEs(DNS aliases) 来从多个主机加载网页的内容。 最大化平行下载不是没有代价的: 因带宽和 CPU 处理速度的不同,太多的平行请求可能会降低性能。 最好的选择是将页面的内容分发到两个主机上,这样比在1,4,10 个主机上请求要有快的速度。 //- 在加载一个 script 的时候平行下载会被阻止,即使是从不同主机加载 网页内容,原因有: 1, scripts 可能会有 document.write 来改变网页的内容; 2, 保证 scripts 以正确的顺序执行 //- 因此最好的选择是把 scripts 放在底部,但是在一些情把 scripts 放在底部不是最好的选择,因为 scripts 中可能有 document.write . //- 一个可能的选择是使用 deferred scripts。 The DEFER attribute indicates that the script does nto contain document.write, and is a clue to browsers that they can continue rendering. // defer 属性暗示 scripts 没有 document.write, 提示浏览器可以继续渲染页面而 不用等到 scripts 加载完 // 但是在 Firefox 中,即使是 deferred scripts 也会阻止渲染网页和平行下载。 // // /** * 需要说明的是:javaScript 是边解析边运行,因此把所有 JavaScript 放在底部的要求就是: * 在加载 JavaScript 之前不会用到加载的 JavaScript 。 * 比如:加载之前不能调用在底部定义的 函数 */ // /** * 因此最好的选择是: * Move scripts to the bottom of the page */ // Rule 7: Avoid CSS Expressions // 不要使用 css expressions // CSS Expressions 只在 IE 5 及之后的版本中支持。在其他浏览器中不支持(被忽略)。 一个 CSS Expressions 的例子: background-color:expression((new Date()).getHours() % / 2 ? red : blue); // width:expression(document.body.clientWidth < 600 ? '600px' : 'auto'); min-width: 600px; // IE 不支持 min-width 属性。 // the expression method accepts a JavaScript expression. The CSS property is set to the result of evaluating the JavaScript expression. // CSS 的 expression 接受一个 JavaScript 的表达式,CSS 属性被设置为这个表达式运算后的值。 // // Problems CSS expressions are re-evaluated when the page changes,such as when is is resized. 严重的是 CSS expressions 计算的次数会很频繁; CSS expressions 不仅在渲染页面和调整大小(resize) 的时候从新计算,而且在滚动页面和移动鼠标的时候也从新计算。 页面的一个 CSS expressions 的计算次数很容易达到 10000+. // 因此 /** * Avoid CSS Expressions */ // Rule 8: Make JavaScripts and CSS External // 首先说明的是:如果一个页面将所有 CSS 和 JavaScript 放在页面内,一般来说: 访问这个页面的速度会比把 CSS 和 JavaScript 放在外部快(没有缓存); 因为他减少了 HTTP Requests,即使 HTTP 允许并行下载。 // 因此把 JavaScript 和 CSS 放在内部还是外部需要做一个权衡。 // 可以从一下几个方面作出决定: // Page Views, Empty Cache vs. Primed Cache, Component Reuse The fewer page views per user, the stronger the argument for inlining JavaScript and CSS. 如果一个页面的 PV 值底的话,可以考虑将 CSS 和 Javacript 放在内部。 // 两个极端的选择: 1,将网站的所有的的 JavaScript 和 CSS 分别放在一个文件中 2,每个页面只放自己的相关的 JavaScript 和 CSS 一个折衷的方法是:将页面分类。 // For home pages that are the first of many page views,inline the JavaScript and CSS for the home page,but leverage external files for all secondary page views. This is accomplished by dynamically downloading the external components in the home page after it has completely loaded. This places the external files in the browser's cache in anticipation fo the user continuing on to other pages. // 对于一个通往一个 pv 值高的页面,可以将 CSS 和 JS 放在内部,然后动态加载 JS 和 CSS 为将来的页面作准备。 // // 说明的是: 后来加载的 css 不能影响原来的 css 后来加载的 js 不能运行,至少用户不知道在运行。 因此可以放在 iframe 中。 <mce:script type="text/javascript"><!-- var myhere = {}; myhere.downloadJS = function( url){ var elem = document.createElement( 'script'); elem.src = url; elem.type = 'text/javascript'; document.body.appendChild( elem); } myhere.downloadCSS = function( url){ var elem = document.createElement( 'link'); elem.rel = 'stylesheet'; elem.type = 'text/css'; elem.href = url; document.body.appendChild( elem); } myhere.downloadComponent = function(){ myhere.downloadJS( 'main.js'); myhere.downloadCSS( 'default.css'); } myhere.onDownload = function(){ setTimeout( myhere.downloadComponent, 1000); } window.onload = myhere.onDownload; // --></mce:script> // Dynamic inlining 服务器根据是否有 cookie 传递过来返回 内部 或 外部 css/js 如果没有对应 cookie,返回 inline;否则返回 external // <mce:script type="text/javascript"><!-- // ... 用到上面的代码加载 myhere.downloadComponent = function(){ // ... document.cookie = 'iscached = 1'; } // --></mce:script> <?php if( isset( $_COOKIE['iscached']) && $_COOKIE['iscached'] == 1 ){ echo <<<HTML <link href="css/default.css" mce_href="css/default.css" rel="stylesheet" type="text/css" /> <mce:script src="javascript/main.js" mce_src="javascript/main.js" type="text/javascript"></mce:script> HTML; // ... }else{ echo "<mce:script type=/"text/javascript/"><!-- /n" . file_get_contents('./javascript/main.js') . " // --></mce:script>/n"; echo "<mce:style type=/"text/css/"><!-- " . file_get_contents('./css/default.css') . " --></mce:style><style type=/"text/css/" mce_bogus="1">" . file_get_contents('./css/default.css') . "</style>"; // ... } ?> // 以上代码有很好的容错性,即使 cookie 或 cache 没有了,页面仍然能正常显示 // /** * Put your JavaScript and CSS in external files */ // Rule 13: Configure RTags/Reconfigure or remove ETags // Entity tags(ETags) are a mechanism that web servers and browsers use to validate cached componets. Etags header thwarts caching when a web site hosted on more than server. // HTTP 规范建议 expires 不要长于一年,虽然浏览器支持超过一年的 expires // 如果缓存的内容的没有 expire,但用户 reload/refresh 网页的时候,浏览器不会直接使用缓存中的内容, 二十现检查缓存是否有效(通过 conditional GET request)。如果没有过期,返回 304(not modified) // 注意是在用户 reload 或 refresh 的时候才会发送请求(没有过期);如果不是二者的话不会发送请求, //直接使用 cache 中的内容。 // 服务器有两种方式来判断内容是否过期: By comparing the last-modified date By comparing the entity tag // ETags 是 HTTP/1.1 中引进的。 An ETag is a string that uniquely identifies a specific version of a component. The only format constraint is that the string must be quoted. // Etag: "e8542bb-2432-bef9a080" 浏览器使用 If-None-Match 传递 ETags 给服务器 // 服务器返回的 Last-Modified 浏览器发送: If-Modified-Since // // The problem with ETags is that they are typically constructed using attributes that make them unique to server hosting a site. 默认情况下 Apache 和 IIS 都会发送 ETags 头。 // Apache 发送的 ETags 的格式是:inode-size-timestamp /** * Inodes are used by filesystems to store information such as file type,owner,group and access mode. */ IIS 的 ETags 的格式是:Filetimestamp:ChangeNumber. /** * ChangeNumber is a counter userd to track configuration chages to IIS. */ // The end results is that ETags generated by Apache and IIS for the exact same component won't match form one server to anther. 结果是由 Apache 或 IIS 产生不同服务器上的同一个文件的 ETages 不同。(如果一个站点只在一个服务器上不会有问题) // If-None-Match header takes precedence over If-Modified-Since. HTTP/1.1 规定:如果有这两个 header 传递给服务器,只有这两个 header 都匹配的话才会返回:304(Not Modified)header. // 解决办法: //-------------- // 使用 PHP 产生 ETags header <?php if( strops( $_SERVER['HTTP_USER_AGENT'], 'MSIE')){ header("ETags:MSIE"); }else{ header("ETags:notMSIE"); } ?> //-------------- // 如果不想这作,可以去掉 ETags header //-------------- Apache 1.3.23 及以后的版本支持 FileEtag,使用这个指令可以将 inode 在 ETags 中移出。 //-------------- // 可以将 ETags 全部移出,因为 If-Last-Modified 足够使用了。 //-------------- // Apache 中移出 Etags 使用 FileETag none //-------------- 因此 /** * Reconfigure or remove ETags */ /** * 这本书读完了,把剩下的 Rule 简单的补上吧。 */ // Rule 1:Make Fewser HTTP Requests // Image Maps CSS Sprites Inline Images Combined Scripts and StyleSheets // //----------------------- // Rule 2: Use a Contend Delivery Network // //----------------------- // Rule 9: Reduce DNS Lookups Reduce DNS lookup by using keep-Alive and fewer domains //----------------------- // Rule 10: Minify JavaScript Minify your JavaScript source code //----------------------- // Rule 11:Avoid Redirects Find ways to avoid redirects //----------------------- // Rule 12 : Remove Dumplicated Scripts Make sure scripts are included only once //----------------------- // Rule 14 : Make Ajax Cacheable Make sure your ajax requests follow the performance guidelines,especially having a far future Expires header