为了改善页面的响应速度,在 rails 里面使用了 HTTP 缓存,做下总结
这里讲的很清楚了,就不多说了
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Caching_FAQ
随便打开一个页面,在 chrome 的 Network 里面查看 request 和 response,可以看到下面的内容
response header
Cache-Control: max-age=0, private, must-revalidate
ETag: W/"25200a77a0ae51ac9511df4ac1e9a0d5"
request header
If-None-Match: W/"64a30229a112c1819d754ef8a7ad8876"
大概意思是,进行 (私有) 浏览器缓存,每次都会验证请求的状态
比较他们的 etag 的值,如果是一样的就返回 304
可以看到默认的 max-age 是 0,我们可以手动指定这个时间
expires_in 1.hour
在 chrome 里面确认,这个时候要注意的是你如果直接更新页面的话无法确认结果
新打开一个 tab, 在 URL 栏里面点击 enter 进入才可以确认
这种比较适合静态页面或者是一段时间不更新也没有任何问题的页面
比如上面设置了 1 个小时的更新,但是你想立刻更新页面的时候
可以在 URL 后面加上时期之类的来更新,比如?20180810120000
headers['ETag'] = Digest::MD5.hexdigest(body)
可以看出是根据每次返回的 response body 来生成的,如果你每次返回的 body 都一样,ETAG 也会一样
一个 API 返回 json 的时候,如果每次返回的值都一样,就会返回 304
默认启用了 csrf, csrf-token 的值每次都会变化,所以基本都是 200
每次都从 response body 来生成 ETAG,明显不是一个高效的方法
因为还是必须去 render html,生成 response body,
虽然可以减少传送的内容,但是服务器端的 response time 没有太大改善
实际应用中,我们很可能需要的是某个或者多个数据未改变,不用去 render html,直接返回 304
这个时候就可以使用 fresh_when
比如有个 book mode,我们希望 book 没有更新对话就返回 304,可以在 controller 里面这样写
def index
fresh_when last_modified: @book.updated_at.utc, etag: @book
fresh_when @book
end
这样的话 book 没有更新的话,就会返回 304
def index
fresh_when [@book, current_user.try(:id)]
end
OR
class TopController < ApplicationController
etag { current_user.try(:id) }
fresh_when @book
end
book 没有更新并且是相同用户的话,才会返回 304
headers[‘Etag’] = Digest::MD5.hexdigest(@book.cache_key)
model 的 cache_key 类似这样books/13-20180517145513000000000
<model name>/<id>-<updated_at>
def etag=(etag)
key = ActiveSupport::Cache.expand_cache_key(etag)
@etag = self[ETAG] = %("#{Digest::MD5.hexdigest(key)}")
end
def retrieve_cache_key(key)
case
when key.respond_to?(:cache_key) then key.cache_key
when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param
when key.respond_to?(:to_a) then retrieve_cache_key(key.to_a)
else key.to_param
end.to_s
end
可以看出最简单的实现自定义类的 etag 生成的方法就是实现 cache_key 方法
比如你有一个类来管理看过的书,看过的书有增加了就生成新的 etag
class BookHistory
def cache_key
# return read book ids
end
end
有时候我们需要禁止缓存,可以这样
def index
expires_now
end
禁止缓存的情况感觉不多,感觉一般没必要用
我在 React(flux + immutable js) 的项目中,API 如果返回 304 的话
在 safari 里面有时候会取不到返回值,暂时采取了 safari 的话就禁止缓存
影响页面变动的变量比较少的时候,我会采用 fresh_when
但是影响页面变动的变量多的话,用 fresh_when 会有一些问题
不知道大家使用时有没有什么好的方法来解决这些问题