浏览器缓存

简单来讲,本地没有文件时,浏览器必然会请求服务器端的内容,并将这部分内容放置在本地的某个缓存目录中。在第二次请求时,它将对本地文件进行检查,如果不能确定这份本地文件是否可以直接使用,它将会发起一次条件请求。所谓条件请求,就是在普通的 GET 请求报文中,附带 If- Modified-Since 字段,如下所示:

If-Modified-Since: Sun, 03 Feb 2013 06:01:12 GMT

它将询问服务器端是否有更新的版本,本地文件的最后修改时间。如果服务器端没有新的版本,只需响应一个304状态码,客户端就使用本地版本。如果服务器端有新的版本,就将新的内容发送给客户端,客户端放弃本地版本。代码如下所示:

var handle = function (req, res) {
fs.stat(filename, function (err, stat) {
var lastModified = stat.mtime.toUTCString();
if (lastModified === req.headers['if-modified-since']) {
res.writeHead(304, "Not Modified");
res.end();
} else {
fs.readFile(filename, function (err, file) {
var lastModified = stat.mtime.toUTCString();
res.setHeader("Last-Modified", lastModified);
res.writeHead(200, "Ok");
res.end(file);
});
}
});
}

这里的条件请求采用时间戳的方式实现,但是时间戳有一些缺陷存在。

  1. 文件的时间戳改动但内容并不一定改动。

  2. 时间戳只能精确到秒级别,更新频繁的内容将无法生效。


为此HTTP1.1中引入了ETag来解决这个问题。ETag 的全称是Entity Tag,由服务器端生成,服务器端可以决定它的生成规则。如果根据文件内容生成散列值,那么条件请求将不会受到时间戳改动造成的带宽浪费。下面是根据内容生成散列值的方法:

var getHash = function (str) {
var shasum = crypto.createHash('sha1');
return shasum.update(str).digest('base64');
};

与 If-Modified-Since/Last-Modified 不同的是,ETag的请求和响应是 If-None-Match/ETag ,如下所示:

var handle = function (req, res) {
fs.readFile(filename, function (err, file) {
var hash = getHash(file);
var noneMatch = req.headers['if-none-match'];
if (hash === noneMatch) {
res.writeHead(304, "Not Modified");
res.end();
} else {
res.setHeader("ETag", hash);
res.writeHead(200, "Ok");
res.end(file);
}
});
};

浏览器在收到 ETag: "83-1359871272000" 这样的响应后,在下次的请求中,会将其放置在请求头中: If-None-Match: "83-1359871272000" 。

尽管条件请求可以在文件内容没有修改的情况下节省带宽,但是它依然会发起一个HTTP请求,使得客户端依然会花一定时间来等待响应。可见最好的方案就是连条件请求都不用发起。那么如何让浏览器知晓是否能直接使用本地版本呢?答案就是服务器端在响应内容时,让浏览器明确地将内容缓存起来。在响应里设置 Expires 或 Cache-Control 头,浏览器将根据该值进行缓存。那么这两个值有何区别呢?

HTTP1.0时,在服务器端设置 Expires 可以告知浏览器要缓存文件内容,如下代码所示:

var handle = function (req, res) {
fs.readFile(filename, function (err, file) {
var expires = new Date();
expires.setTime(expires.getTime() + 10 * 365 * 24 * 60 * 60 * 1000);
res.setHeader("Expires", expires.toUTCString());
res.writeHead(200, "Ok");
res.end(file);
});
};

Expires 是一个GMT格式的时间字符串。浏览器在接到这个过期值后,只要本地还存在这个缓存文件,在到期时间之前它都不会再发起请求。

但是 Expires 的缺陷在于浏览器与服务器之间的时间可能不一致,这可能会带来一些问题,比如文件提前过期,或者到期后并没有被删除。在这种情况下, Cache-Control 以更丰富的形式,实现相同的功能,如下所示:

var handle = function (req, res) {
fs.readFile(filename, function (err, file) {
res.setHeader("Cache-Control", "max-age=" + 10 * 365 * 24 * 60 * 60 * 1000);
res.writeHead(200, "Ok");
res.end(file);
});
};


上面的代码为 Cache-Control 设置了 max-age 值,它比 Expires 优秀的地方在于,Cache-Control 能够避免浏览器端与服务器端时间不同步带来的不一致性问题,只要进行类似倒计时的方式计算过期时间即可。除此之外, Cache-Control 的值还能设置 public、 private 、 no-cache 、 no-store 等能够更精细地控制缓存的选项。

由于在HTTP1.0时还不支持 max-age ,如今的服务器端在模块的支持下多半同时对 Expires 和 Cache-Control 进行支持。在浏览器中如果两个值同时存在,且被同时支持时, max-age 会覆盖 Expires 。


640?wx_fmt=png


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值