h5离线缓存
1. 协议缓存
举个例子:
更新了一张图片,发布之后反复重新进页面总是看不到更新
说明是协议缓存?
http协议缓存机制是指通过HTTP协议头里的Cache-Control(或 Expires)和Last-Modified(或Etag)等字段来控制文件缓存的机制。
- Cache-Control 用于控制文件在本地缓存有效时长。最常见的,比如服务器回包:Cache-Control:max-age=600 表示文件在本地应该缓存,且有效时长是600秒(从发出请求算起)。在接下来600秒内,如果有请求这个资源,浏览器不会发出 HTTP 请求,而是直接使用本地缓存的文件。
- Last-Modified 是标识文件在服务器上的最新更新时间。下次请求时,如果文件缓存过期,浏览器通过 If-Modified-Since 字段带上这个时间,发送给服务器,由服务器比较时间戳来判断文件是否有修改。如果没有修改,服务器返回304告诉浏览器继续使用缓存;如果有修改,则返回200,同时返回最新的文件。
Cache-Control 还有一个同功能的字段:Expires。Expires 的值一个绝对的时间点,如:Expires: Thu, 10 Nov 2015 08:45:11 GMT,表示在这个时间点之前,缓存都是有效的。
Expires 是 HTTP1.0 标准中的字段,Cache-Control 是 HTTP1.1 标准中新加的字段,功能一样,都是控制缓存的有效时间。当这两个字段同时出现时,Cache-Control 是高优化级的。
Etag 也是和 Last-Modified一样,对文件进行标识的字段。不同的是,Etag的取值是一个对文件进行标识的特征字串。在向服务器查询文件是否有更新时,浏览器通过If-None-Match 字段把特征字串发送给服务器,由服务器和文件最新特征字串进行匹配,来判断文件是否有更新。没有更新回包304,有更新回包200。Etag和Last-Modified可根据需求使用一个或两个同时使用。两个同时使用时,只要满足其中一个条件,就认为文件没有更新。
不过有两种情况比较特殊:
- 手动刷新页面(F5):
浏览器会直接认为缓存已经过期(可能缓存还没有过期),在请求中加上字段:Cache-Control:max-age=0,发包向服务器查询是否有文件是否有更新。 - 强制刷新页面(Ctrl+F5):
浏览器会直接忽略本地的缓存(有缓存也会认为本地没有缓存),在请求中加上字段:Cache-Control:no-cache(或Pragma:no-cache),发包向服务重新拉取文件。
2. 应用缓存(离线缓存)
除了http协议缓存,HTML5 提供一种应用程序缓存机制,使得基于web的应用程序可以离线运行。为了能够让用户在离线状态下继续访问 Web 应用,开发者需要提供一个 cache manifest 文件。这个文件中列出了所有需要在离线状态下使用的资源,浏览器会把这些资源缓存到本地。
虽然manifest的技术已被web标准废弃, 但这不影响我们尝试去了解它. 也正是因为manifest的应用缓存机制如此诱人, 饿了么 和 office 365邮箱等都还在使用着它!
使用方法:
<!-- index.html -->
<!DOCTYPE HTML>
<html manifest="test.manifest">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/appactivity/testmain/css/index.css">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
我是哈哈哈哈哈
</body>
</html>
注意事项:
- 如何开启缓存:
<html manifest="test.manifest">
...
</html>
在html标签中指定manifest文件, 便表示该网页使用manifest进行离线缓存. 该网页内需要缓存的文件列表需要在 test.manifest 文本文件中指定.
- manifest缓存清单
就像写作文一样, manifest采用经典的三段式. 分别为: CACHE, NETWORK 和 FALLBACK. 如下, 先看一个栗子?:
#test.manifest文件
CACHE MANIFEST
# Time: Sat Jun 04 2016 17:11:50 GMT+0800 (CST)
# webpack-multipage v1.0.0==== ========
CACHE:
css/index.css
NETWORK:
js/app.js
FALLBACK:
/other 404.html
第一行必须以 CACHE MANIFEST 开头, 后可跟若干字符注释, 注释从#号开始. 跟在 CACHE MANIFEST 行后的文件。
第一段内容以 CACHE: 开头
每行列出一个, 这些文件是需要缓存的文件. 因此 index.css 会被缓存, 不需要访问网络.
第二段内容以 NETWORK: 开始
跟在该行后的文件表示需要访问网络. 如: app.js 将直接从网络上下载, 并不走manifest cache, 如果除了第一段中缓存的文件以外, 其他文件都从网络上获取, 那么此时可将 app.js 改为 * (通配符).
第三段内容以 FALLBACK: 开始
跟在该行后的文件表示会有一个替代方案. 如: 当访问 /other 路径时, 如果访问失败, 那么将自动加载 404.html 作为替代.
- manifest缓存状态
每个manifest缓存都有一个状态, 标示着缓存的情况. 一份缓存清单只有一个缓存状态, 即使它被多个页面引用. 以下是各个缓存状态:
- UNCACHED(未缓存): 表明应用缓存对象还没有初始化完成.
- IDLE(空闲): 应用缓存并未处于更新状态.
- CHECKING(检查): 正在检查是否存在更新.
- DOWNLOADING(下载): 清单更新后, 重新下载全部资源到临时缓存中.
- UPDATEREADY(更新就绪): 新版本的缓存下载完成, 全部就绪, 随即触发事件 updateready.
- OBSOLETE(废弃): 应用缓存已被废弃.
- applicationCache
//webview下
var cache = window.applicationCache;
//shared worker中
var cache = self.applicationCache;
以下是属性和方法:
-
status: 返回当前页面的应用缓存的状态, 通常开启应用缓存的页面可能返回1, 其他页面则返回0.
-
update(): 手动触发应用缓存的更新.
注意:
(1) 若有更新, 则依次触发①检查事件(Checking event), ②下载事件(Downloading event), ③下载进度事件(Progress event), ④更新完成事件(UpdateReady event);
(2) 若无更新, 则依次触发①检查事件(Checking event), ②无更新事件(NoUpdate event);
(3) 在未开启应用缓存的页面调用将抛出Uncaught DOMException 错误.
update() 方法通常在长时间不关闭的页面使用, 比如说邮箱应用, 用于定期检测可能的更新.
- abort(): 取消应用缓存的更新. 可用于节省有限的网络带宽.
- swapCache(): 如果存在一个更新版本的应用缓存, 那么它将切换过去, 否则将抛出 Uncaught DOMException 错误. 通常, 我们会在updateready事件触发之后手动调用swapCache()方法, swapCache的切换只对后续加载的缓存文件有效, 已经加载成功的资源并不会重新加载.
那么如何利用好上述api更新一个页面的应用缓存呢? 别急, Beginner’s Guide to Using the Application Cache 一文中提供了如下的样板方法:
// Check if a new cache is available on page load.
window.addEventListener('load', function(e) {
window.applicationCache.addEventListener('updateready', function(e) {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
// Browser downloaded a new app cache.
// Swap it in and reload the page to get the new hotness.
window.applicationCache.swapCache();
if (confirm('A new version of this site is available. Load it?')) {
window.location.reload();
}
} else {
// Manifest didn't changed. Nothing new to server.
}
}, false);
}, false);
-
服务器配置方法
nginx:
- 找到Ngnix服务器配置文件mime.types
- 编辑mime.types
- 添加manifest文件映射
types {
text/html html htm shtml;
text/css css;
text/xml xml rss;
image/gif gif;
image/jpeg jpeg jpg;
application/x-javascript js;
application/atom+xml atom;
text/mathml mml;
text/plain txt;
text/vnd.sun.j2me.app-descriptor jad;
text/vnd.wap.wml wml;
text/x-component htc;
image/png png;
image/tiff tif tiff;
image/vnd.wap.wbmp wbmp;
image/x-icon ico;
image/x-jng jng;
image/x-ms-bmp bmp;
image/svg+xml svg;
application/java-archive jar war ear;
application/mac-binhex40 hqx;
application/msword doc;
application/pdf pdf;
application/postscript ps eps ai;
application/rtf rtf;
application/vnd.ms-excel xls;
application/vnd.ms-powerpoint ppt;
application/vnd.wap.wmlc wmlc;
application/vnd.wap.xhtml+xml xhtml;
application/x-cocoa cco;
application/x-java-archive-diff jardiff;
application/x-java-jnlp-file jnlp;
application/x-makeself run;
application/x-perl pl pm;
application/x-pilot prc pdb;
application/x-rar-compressed rar;
application/x-RedHat-package-manager rpm;
application/x-sea sea;
application/x-shockwave-flash swf;
application/x-stuffit sit;
application/x-tcl tcl tk;
application/x-x509-ca-cert der pem crt;
application/x-xpinstall xpi;
application/zip zip;
application/octet-stream bin exe dll;
application/octet-stream deb;
application/octet-stream dmg;
application/octet-stream eot;
application/octet-stream iso img;
application/octet-stream msi msp msm;
audio/midi mid midi kar;
audio/mpeg mp3;
audio/x-realaudio ra;
video/3gpp 3gpp 3gp;
video/mpeg mpeg mpg;
video/quicktime mov;
video/x-flv flv;
video/x-mng mng;
video/x-ms-asf asx asf;
video/x-ms-wmv wmv;
video/x-msvideo avi;
application/x-nokia-widget wgz;
text/cache-manifest mf manifest;
}
4. 重启nginx
注意: manifest后面一定要加 ;
- manifest缓存规则
- 遵循全量缓存的规律. 即: manifest文件改动后, 将重新缓存一遍所有的文件(包括html本身和动态添加的需要缓存的文件,即使缓存列表中没有该html). 第一次缓存过程中如果出现缓存失败的文件, 那么, 第二访问, 又将重新缓存一遍所有的文件. 以此类推.
- manifest文件本身不能写进缓存清单, 否则连同html和资源在其缓存失效之前, 将永远不能获得更新.
- 即使manifest文件丢失, 缓存依然有效. 不过从此以后, 引入该manifest的html, 将永远不能获得更新.
- webview的缓存现象
通常, webview的缓存有如下三种现象:
- 普通网页(无manifest文件), 不受manifest缓存影响, 缓存只走 http cache.
- 包含manifest文件的网页, 缓存文件只受manifest缓存影响(只有manifest文件改变时才会更新缓存资源), 缓存资源完全与 http cache 无关, 但是 NETWORK 段落后需要访问网络的文件, 将继续走 http cache.
- webview直接加载manifest缓存过的文件时, 优先加载第一个manifest缓存的该文件, 如果没有找到manifest缓存, 那么它将自动寻找 http cache 或者 在线加载.
- 最佳实践
- 通常只使用一个manifest文件, 并保证缓存的文件尽可能的少, 以减小manifest每次更新清单中文件所耗费的时间和流量.
- 如果一定要使用两个及以上manifest文件, 缓存文件请尽量不要相同.
- 如果以上两条都不能保证, 那么, 请保证尽可能在manifest缓存的状态更新时, 主动去刷新网页.(此时并不能保证不同网页之间同一个缓存文件版本一致)
- 具体落地步骤
- 如果缓存的文件需要加参数运行, 建议将参数内容加到hash中, 如:cached-page.html#parameterName=value
- manifest 的引入可以使用绝对路径或者相对路径, 如果你使用的是绝对路径, 那么你的manifest文件必须和你的站点处于同一个域名下.
- manifest文件你可以保存为任意的扩展名, 但是响应头中以下字段须取以下定值, 以保证manifest文件正确被解析, 并且它没有http缓存
Content-Type: text/cache-manifest
Cache-Control: max-age=0
Expires: [CURRENT TIME]
- 总结
- 离线缓存与传统浏览器缓存区别:
浏览器缓存(Browser Caching)是为了节约网络的资源加速浏览,浏览器在用户磁盘上对最近请求过的文档进行存储,当访问者再次请求这个页面时,浏览器就可以从本地磁盘显示文档,这样就可以加速页面的阅览
区别:
-
离线缓存是针对整个应用,浏览器缓存是单个文件
-
离线缓存断网了还是可以打开页面,浏览器缓存不行
-
离线缓存可以主动通知浏览器更新资源
本地存储和离线存储有什么不同
-
本地存储与离线缓存都是为了方便网页的加载,提高用户体验等。
-
本地存储一般存储的都是数据,而离线缓存一般存储的是网页等。
- 试用场景
- 单地址的页面
- 对实时性要求不高的业务
- 离线的webapp
- 优点
- 完全离线
- 资源被缓存,加载更快
- 降低server负载
- 缺点
- 含有manifest属性的当前请求页无论如何都会被缓存;
- 更新需要建立在manifest文件的更新,文件更新后是需要页面再次刷新的(需要2次刷新才能获取资源);
- 更新是全局性的,无法单独更次年某个文件,即一个文件被更新,需要拉取全部文件来更新;
- 对于链接的参数变化是敏感的,任何一个参数的修改都会被(master)重新缓存(重复缓存含参页面)index.html和Index.html?renew=1会被认为是不同文件,分别缓存