stale-while-revalidate 缓存策略

stale-while-revalidate 缓存策略

Mar 15, 2023 ·
4 Min Read

stale-while-revalidate (简称SWR) 是 HTTP 的一种缓存策略,是 HTTP 的响应头 cache-control的一个属性值。 一种由 HTTP RFC 5861(opens in a new tab) 推广的 HTTP 缓存失效策略。这种策略首先从缓存中返回数据(过期的),同时发送 fetch 请求(重新验证),最后得到最新数据。

如果你有使用过 NEXTjsSWR 的话,其中都有用到 stale-while-revalidate,比如 nextjs getStaticProps 返回的参数 revalidate 就是设置这个属性。

浏览器 SWR 缓存策略主要分为两部分

  1. 判断响应缓存是否过期。
  2. 重新校验响应数据时效性。

📨 例子

假设有一请求的响应头有如下字段👇🏻

Cache-Control: max-age=600, stale-while-revalidate=30

那么使用它的优点是❓❓❓ 在传统的同步更新缓存机制中,一个资源的缓存过期之后,如果想再次使用它,需要先对该缓存进行 revalidate。在 revalidate 执行期间,客户端就得等待,直到 revalidate 请求结束。 而 stale-while-revalidate 可以进行后台缓存刷新,提升加载速度

没有十全十美的东西,自然它也不例外👇🏻 stale-while-revalidate 进行异步缓存更新,因此 revalidate 期间时返回上一次的缓存结果,这也导致会牺牲一次(通常)资源的新鲜性

实操案例

服务端代码

const http = require('http')
const app = http.createServer()
let firstRequest = true
app.on('request', (req, res) => {
const { url } = req
const { host } = req.headers
const route = new URL(url, `http://${host}`).pathname
let response = {
stat: 0,
data: {}
}
switch (route) {
case '/api/list':
// 模拟请求后一次后,响应数据改变
if(firstRequest) {
response = {
stat: 1,
data: '第一次请求的数据'
}
firstRequest = false
} else {
response = {
stat: 1,
data: '最新的数据'
}
}
break
default:
break
}
res.setHeader("Access-Control-Allow-Origin", "*")
res.setHeader('Content-Type', 'application/json; charset=utf-8')
res.setHeader('Cache-Control', 'max-age=30, stale-while-revalidate=10')
res.end(JSON.stringify(response))
})
app.listen(8888, () => {
console.log('webserver running')
})

客户端代码

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div>第一次请求的数据:<span id="first"></span></div>
<div>30s~40s请求的数据:<span id="second"></span></div>
<div>40s后请求的数据:<span id="third"></span></div>
<script>
async function fetchData() {
const res = await (await fetch('http://localhost:8888/api/list')).json()
return res
}
async function render() {
const firstRes = await fetchData()
document.getElementById('first').innerText = JSON.stringify(firstRes)
setTimeout(async () => {
// 31秒后自动请求
const secondRes = await fetchData()
document.getElementById('second').innerText = JSON.stringify(secondRes)
}, 31000)
setTimeout(async () => {
// 41秒后自动请求
const secondRes = await fetchData()
document.getElementById('third').innerText = JSON.stringify(thirdRes)
}, 41000)
}
render()
</script>
</body>
</html>

运行结果👇🏻

第一次请求的数据:{stat: 1,data: '第一次请求的数据'}

30s~40s请求的数据:{stat: 1,data: '第一次请求的数据'}

40s后请求的数据:{stat: 1,data: '最新的数据'}.

Last edited Feb 15