最近看了很多關於客戶端緩存機制的文章,大概弄明白了整個原理,可是對於中間一些細節一直有點迷糊,下面的內容是本身作的驗證javascript
前端之瀏覽器緩存,一次搞定html
Expires
Cache-Control
Expires
proxy_cache_valid
proxy_cache_path
中的inactive
當前這篇會先驗證無代理服務器的狀況,有代理的狀況放在以後驗證java
原服務器的Cache-Control
:node
名稱 | 說明 |
---|---|
private | 客戶端能夠緩存,代理服務器不能夠緩存 |
public | 客戶端和代理服務器均可以緩存 |
max-age=t | 緩存內容將在t秒後失效 |
no-cache | 使用協商緩 |
no-store | 不使用緩存 |
原服務器的Expires
:nginx
這是http 1.0的屬性,如今應該用的少了;該屬性設置的是一個過時時間,過時時間內命中強緩存;過時時間外,協商緩存chrome
如下是我準備驗證的問題:express
Cache-Control
不一樣屬性的實際狀況Expires
的實際狀況Expires
和Cache-Control
同時存在,那個優先級高[1-24 更新] 增長一個驗證:json
驗證方式是使用node的express啓動一個服務後端
const fs = require('fs');
const path = require('path');
const express = require('express');
const app = express();
const port = 3030;
app.use(express.static(path.resolve(__dirname, './')));
app.get('/index', (req, res) => {
const html = fs.readFileSync(path.resolve(__dirname, './index.html'), 'utf-8');
res.send(html)
})
// cache驗證
app.all('*', (req, res, next) => {
// res.header('Cache-Control', 'private');
// res.header('Cache-Control', 'public');
res.header('Cache-Control', 'max-age=20');
// res.header('Cache-Control', 'no-cache');
// res.header('Cache-Control', 'no-store');
next();
})
const questions = [
{
id: '000',
name: 'Rose'
},
{
id: '111',
name: 'Jack'
}
]
app.get('/api/data', (req, res) => {
res.status(200);
res.json(questions);
})
// 監聽端口
app.listen(port, () => {
console.log(`success listen at ${port}`);
})
複製代碼
ps:由於不存在代理服務器,因此public和private的區別如今是看不出來的,咱們以後和有代理的狀況一塊兒驗證
// 單獨驗證Cache-Control
app.all('*', (req, res, next) => {
res.header('Cache-Control', 'max-age=30');
// res.header('Cache-Control', 'no-cache');
// res.header('Cache-Control', 'no-store');
next();
})
複製代碼
如下驗證能夠得出結論:
Cache-Control | 請求延遲時間 | 資源是否改變 | If-None-Match(request) | ETag(respanse) | Status Code | 結論 |
---|---|---|---|---|---|---|
max-age=30 | 10s | 是 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 200 OK (from disk cache) | 強緩存未過時,命中強緩存 |
max-age=30 | 60s | 是 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo" | 200 OK | 強緩存過時,資源被修改,從新從服務器獲取資源 |
max-age=30 | 10s | 否 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 200 OK (from disk cache) | 強緩存未過時,命中強緩存 |
max-age=30 | 60s | 否 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 304 Not Modified | 強緩存過時,資源未修改,命中協商緩存 |
no-cache | 30s | 是 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo" | 200 OK | 資源被修改,未命中協商緩存,從服務器獲取資源 |
no-cache | 30s | 否 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 304 Not Modified | 資源未修改,命中協商緩存 |
no-store | 30s | 是 | 無 | W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo" | 200 OK | 不使用緩存,直接從服務端獲取資源 |
no-store | 30s | 否 | 無 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 200 OK | 不使用緩存,直接從服務端獲取資源 |
ps1:若是這個Expires
字段後端沒有處理過的話,返回的應該是GMT的標準時間Wed, 23 Jan 2019 09:52:55 GMT
,也就是格林威治的標準時間;而咱們通常使用的是本地時間Wed Jan 23 2019 18:06:15 GMT+0800 (中國標準時間)
,因此要作一下適當的處理
ps2:我是在chrome上用localhost上測試的,但不知道爲何設置Expires
後,無論過沒過時,無論有沒有同時設置Cache-Control
,Status Code
狀態一直是304的,據說好像是由於用了localhost的關係,這個與線上並不必定徹底一致;這裏關於Expires
的測試我是加了nginx代理了以後的結果,不過代理的緩存沒有設置
ps3:Expires
的結果就僅供參考吧,以上
const moment = require('moment');
// cache驗證
app.all('*', (req, res, next) => {
res.header('Expires', getGLNZ());
next();
})
// 轉換格林威治時間
function getGLNZ() {
return moment().utc().add(30, 's').format('ddd, DD MMM YYYY HH:mm:ss') + ' GMT';
}
複製代碼
如下驗證能夠得出結論:
Expires | 請求延遲時間 | 資源是否改變 | If-None-Match(request) | ETag(respanse) | Status Code | 結論 |
---|---|---|---|---|---|---|
當前時間+30s | 10s | 否 | 無 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 200 OK (from disk cache) | 強緩存未過時,命中強緩存 |
當前時間+30s | 60s | 否 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 304 Not Modified | 強緩存過時,資源未修改,命中協商緩存 |
當前時間+30s | 10s | 是 | 無 | W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo" | 200 OK (from disk cache) | 強緩存未過時,命中強緩存 |
當前時間+30s | 60s | 是 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo" | 200 OK | 強緩存過時,資源被修改,從新從服務器獲取資源 |
結論:Cache-Control
優先級比Expires
優先級高
const moment = require('moment');
// cache驗證
app.all('*', (req, res, next) => {
res.header('Cache-Control', 'max-age=60');
// res.header('Cache-Control', 'no-cache');
// res.header('Cache-Control', 'no-store');
res.header('Expires', getGLNZ());
next();
})
function getGLNZ(){
return moment().utc().add(30, 's').format('ddd, DD MMM YYYY HH:mm:ss') + ' GMT';
}
複製代碼
如下驗證能夠看出,在Cache-Control
與Expires
同時設置的狀況下,Expires
是失效的
Expires | Cache-Control | 請求延遲時間 | 資源是否改變 | If-None-Match(request) | ETag(respanse) | Status Code | 結論 |
---|---|---|---|---|---|---|---|
當前時間+30s | max-age=60 | 10s | 否 | 無 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 200 OK (from disk cache) | Expires與Cache-Control都未過時,命中強緩存 |
當前時間+30s | max-age=60 | 40s | 否 | 無 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 200 OK (from disk cache) | Expires過時,Cache-Control未過時,命中強緩存 |
當前時間+30s | max-age=60 | 100s | 否 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 304 Not Modified | Expires與Cache-Control都過時,資源未修改,命中協商緩存 |
當前時間+30s | no-cache | 10s | 否 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 304 Not Modified | Expires未過時,可是命中協商緩存 |
當前時間+30s | no-cache | 60s | 否 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 304 Not Modified | Expires過時,命中協商緩存 |
當前時間+30s | no-store | 10s | 否 | 無 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 200 OK | Expires未過時,直接獲取服務端資源 |
當前時間+30s | no-store | 60s | 否 | 無 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 200 OK | Expires過時,直接獲取服務端資源 |
如下驗證結論:
If-None-Match
與ETag
相同 -> 協商緩存If-None-Match
與ETag
不相同 -> 服務器獲取資源資源是否改變 | If-None-Match(request) | ETag(respanse) | Status Code | 結論 |
---|---|---|---|---|
是 | W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo" | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 200 OK | 服務器獲取資源 |
否 | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" | 304 Not Modified | 命中協商緩存 |
Cache-Control
Expires
Cache-Control
與Expires
同時存在Cache-Control
與Expires
都未設置request
中的If-None-Match
與response
中的ETag
If-None-Match
與ETag
相同 -> 協商緩存If-None-Match
與ETag
不相同 -> 服務器獲取資源Cache-Control no-cahce