网页性能优化:设置永久缓存
2015年6月1日

当浏览器请求一个之前访问过的静态文件时,会在HTTP头中加上:If-Modified-SinceIf-None-Match以向服务器确认该文件是否有更新,如果没有更新,服务器则返回304 Not Modified,浏览器就会使用本地缓存而不是从服务器重新下载该文件。这是多数Web Server对于静态文件的默认缓存行为,然而这个缓存行为仍有优化空间。默认的缓存行为依然需要浏览器与服务器建立一个HTTP连接以确定每个文件是否更新过,从客户端建立连接、发送连接、服务器判断文件状态到最终客户端收到304 Not Modified之间仍需要几十甚至上百毫秒并且消耗一定的服务器资源。

第一次访问网站:

第二次访问网站:

从上面两个图可以看到,虽然第二次访问静态文件全都使用了本地缓存,但是全部资源加载任然花了将近1秒,每个HTTP 304连接都需要150毫秒左右。

其实这些步骤可以全部省略,我们可以强制浏览器直接使用本地的缓存,这样就不存在任何网络的传输和服务器消耗了。

Cache-Control & Expires

Cache-Control可以通过设置max-age值来指定缓存的有效期(单位:秒)。

Cache-Control: max-age=3153600

Expires可以指定一个特定的缓存过期时间,效果同Cache-Control的max-age一样。

Expires: Fri, 01 Jul 2016 00:00:00 GMT

使用了上述任一方法后,浏览器在缓存过期前将一直使用本地缓存而不再与服务器确认此文件是否有更新,整体加载时间显著降低。

如何更新文件?

这种极端缓存策略的弊端显而易见:浏览器将一直使用本地缓存而导致得不到更新。解决方法其实很简单,浏览器是根据文件的URL来判断是否使用缓存的,那么当文件更新后,只要确保html内文件URL发生变化,浏览器就会从服务器重新下载这个文件,当然我们一定要确保html文件本身没有设置那么长的缓存时间。文件URL大致可以有下面几种变化模式:

  1. 项目自动打包时生成随机的文件名
    <script type="text/javascript" src="../src/js/febfcddf6bc65078231ce9026c2b96ad.js"></script>
  2. 文件名中加入版本号
    <script type="text/javascript" src="../src/js/lib/jquery-1.11.3.min.js"></script>
  3. URL中加入版本参数
    <script type="text/javascript" src="../src/js/default.js?v=2"></script>

如何添加Cache-Control?

IIS

  1. 选择你要添加 Cache-Control 的文件目录,双击 HTTP Response Headers

  2. 单击 Set Common Headers...,勾选 Expire Web content 并设置你想要的 max-age 值。

  3. 你也可以直接编辑文件目录下的 web.config 来设置Cache-Control。

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
     <system.webServer>
         <staticContent>
             <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="365.00:00:00" />
         </staticContent>
     </system.webServer>
    </configuration>

Apache

  1. 编辑.htaccess文件:
    # 1 year for all your static assets
    <filesMatch ".(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf)$">
    Header set Cache-Control "max-age=31536000, public"
    </filesMatch>

NodeJS

  • 对于使用 Express Static 中间件,max-age 设置的单位是毫秒。

    var oneYear = 60 * 1000 * 60 * 24 * 365;
    app.use(express.static('staticFile', { maxAge: oneYear }));
  • 原生NodeJS可以使用 setHeader 方法来添加 Cache-Control

    response.setHeader('Cache-Control', 'max-age=31536000');