一、浏览器打开一个新页面的过程
image.png如上图所示,我们来看看每一部分分别都做了什么:
-- prompt for unload: 卸载旧页面,请求新页面
---- navigationStart: 开始记时,开始处理请求页面的起始时间
-- redirect: 定向url
-- unload: 继续卸载旧页面
-- Appcache: 检查是否有离线缓存
以上操作全是在本地完成的,下面一步才开始连接网络
-- DNS
---- domainLookupStart: 开始域查找
---- domainLookupEnd: IP地址翻译结束
-- TCP:开启TCP连接,在请求资源时,是复用一个tcp连接的,以进行js,css等的串行下载
---- responseEnd: 响应完成,开始处理数据,需要注意的是,在响应过程中是不处理数据的,响应完成后才开始处理数据
-- Processing: 浏览器开始工作
---- domLoading: 将dom载入到内存当中
---- domInteractive: 解析dom,创建dom树
---- domInteractive~domContentLoaded: 这个过程中继续拿取css、js、图片资源等,到domContentLoaded时dom内容全部处理完成
---- domContentLoaded~domComplete: 这个过程中做一些其它事情,如提取页面信息,设置缓存参数等,到domComplete时dom处理结束
-- onLoad: html标签中的onLoad,开始执行脚本,处理函数等
我们需要知道的是,从prompt for unload一直到Processing页面一直是白的。我们可以根据上图来考虑需要优化的几个点:
- 缓存
- CDN
- TCP连接时间,加大带宽,服务器端响应速度
- 请求,响应速度,数据量越小,响应越快
- Processing,优化dom结构
二、缓存
缓存会根据请求保存输出内容的副本,例如html页面,图片,文 件,当下一个请求来到的时候:如果是相同的URL,缓存直接使 用副本响应访问请求,而不是向源服务器再次发送请求。
我们先来看一下浏览器没有缓存,或者第一次请求时的情况,如下所示:
image.png
接下来是有缓存的情况,如下所示:
image.png
我们先来解释下上图中几个缓存的意思:
image.png
- last-modified / if-modified-since
这是一组请求/相应头
响应头:* last-modified: Wed, 16 May 2018 02:57:16 GMT 01
请求头:if-modified-since: Wed, 16 May 2018 05:55:38 GMT
last-modified标示这个响应资源的最后修改时间,服务器端返回资源时,如果头部带上了 last-modified,那么资源下次请求时就会把值加入到请求头 if-modified-since 中,服务器可以对比这个值,确定资源是否发生变化,如果没有发生变化,则返回 304。
当资源过期时(使用Cache-Control标识的max-age),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头If- Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified- Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。 - etag / if-none-match
这也是一组请求/相应头
响应头:etag: "D5FC8B85A045FF720547BC36FC872550"
请求头:if-none-match: "D5FC8B85A045FF720547BC36FC872550"
etag是浏览器当前资源在服务器的唯一标识(生成规则由服务器决定),服务器端返回资源时,如果头部带上了 etag,那么资源下次请求时就会把值加入到请求头 if-none-match 中,服务器可以对⽐这个值,确定资源是否发生变化,如果没有发生变化,则返回 304。
当资源过期时(使用Cache-Control标识的max- age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match (Etag的值)。web服务器收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对,决定返回200或304。 - expires
* expires: Thu, 16 May 2019 03:05:59 GMT
在http头中设置一个过期时间,在这个过期时间之前,浏览器的请求都不会发出,而是自动从缓存中读取文件,除非缓存被清空,或者强制刷新。缺陷在于,服务器时间和⽤户端时间可能存在不⼀致,所以 HTTP/1.1 加⼊了 cache-control 头来改进这个问题。 - cache-control
设置过期的时间长度(秒),在这个时间范围内,浏览器请求都会直接读缓存。当 expires 和 cache-control 都存在时,cache-control 的优先级更高。
这几个缓存的优先级如下:
cache-control > expires > etag > last-modified
- keep-alive
三、node中设置缓存
// last-modified
// 缺陷:文件的时间戳改动了但内容不一定改动。时间戳只能精确到秒级别,更新频繁的内容将无法生效。
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);
});
}
});
};
// etags
var getHash = function (str) {
var shasum = crypto.createHash('sha1');
return shasum.update(str).digest('base64');
};
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);
}
});
};
// 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);
});
};
// 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);
});
};
四、nginx配置缓存
首先打开nginx.conf文件
gzip on // 开启压缩
etag on // 开启etag
expires 30d // 过期时间
add_header Cache-Control no-cache // 设置过期时间时要同时在下面设置这句,no-cache不要浏览器缓存
网友评论