导读
Manifest 是 H5提供的一种应用缓存机制, 基于它web应用可以实现离线访问(offline cache). 为此, 浏览器还提供了应用缓存的api–applicationCache. 虽然manifest的技术已被web标准废弃, 但这不影响我们尝试去了解它. 也正是因为manifest的应用缓存机制如此诱人, 饿了么 和 office 365邮箱等都还在使用着它!
描述
对manifest熟悉的同学可以跳过此节.
鉴于manifest应用缓存的技术, 我们可以做到:
- 离线访问: 即使服务器挂了, 或者没有网络, 用户依然可以正常浏览网页内容.
- 访问更快: 数据存在于本地, 省去了浏览器发起http请求的时间, 因此访问更快, 移动端效果更为明显.
- 降低负载: 浏览器只在manifest文件改动时才去服务器下载需要缓存的资源, 大大降低了服务器负载.
manifest缓存的过程如下(来自网络):
支持性
主流浏览器都支持manifest应用缓存技术. 如下表格:
IE | Edge | Firefox | Chrome | Safari | Opera | ios | Android |
---|---|---|---|---|---|---|---|
10+ | 12+ | 3.5+ | 4+ | 4+ | 11.5+ | 7.1+ | 2.3+ |
H5标准中, Offline Web applications 部分有如下描述:
This feature is in the process of being removed from the Web platform. (This is a long process that takes many years.) Using any of the offline Web application features at this time is highly discouraged. Use service workers instead. [SW]
因此后续我将在其他文章中继续介绍 service workers, 本篇继续关注manifest.
如何开启应用缓存
manifest使用缓存清单进行管理, 缓存清单需要与html标签进行关联. 如下:
<html manifest="test.appcache">
...
</html>
在html标签中指定manifest文件, 便表示该网页使用manifest进行离线缓存. 该网页内需要缓存的文件列表需要在 test.appcache 文本文件中指定.
manifest缓存清单
就像写作文一样, manifest采用经典的三段式. 分别为: CACHE
, NETWORK
和 FALLBACK
. 如下, 先看一个栗子🌰:
CACHE MANIFEST
# v1.0.0
content.css
NETWORK:
app.js
FALLBACK:
/other 404.html
其中第一行必须以 CACHE MANIFEST
开头, 后可跟若干字符注释, 注释从#号开始. 跟在 CACHE MANIFEST
行后的文件, 每行列出一个, 这些文件是需要缓存的文件. 因此 content.css 会被缓存, 不需要访问网络.
第二段内容以 NETWORK:
开始, 跟在该行后的文件表示需要访问网络. 如: app.js 将直接从网络上下载, 并不走manifest cache, 如果除了第一段中缓存的文件以外, 其他文件都从网络上获取, 那么此时可将 app.js 改为 * (通配符).
第三段内容以 FALLBACK:
开始, 跟在该行后的文件表示会有一个替代方案. 如: 当访问 /other 路径时, 如果访问失败, 那么将自动加载 404.html 作为替代.
manifest缓存状态
每个manifest缓存都有一个状态, 标示着缓存的情况. 一份缓存清单只有一个缓存状态, 即使它被多个页面引用. 以下是各个缓存状态:
- UNCACHED(未缓存): 表明应用缓存对象还没有初始化完成.
- IDLE(空闲): 应用缓存并未处于更新状态.
- CHECKING(检查): 正在检查是否存在更新.
- DOWNLOADING(下载): 清单更新后, 重新下载全部资源到临时缓存中.
- UPDATEREADY(更新就绪): 新版本的缓存下载完成, 全部就绪, 随即触发事件 updateready.
- OBSOLETE(废弃): 应用缓存已被废弃.
上述缓存状态常量依次取值0, 1, 2, 3, 4, 5.
applicationCache
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);
manifest缓存独立性
- manifest的缓存和浏览器默认的缓存是两套机制, 相互独立, 并且不受浏览器缓存大小限制(Chrome下测试结果).
- 各个manifest文件的缓存相互独立, 各自在独立的区域进行缓存. 即使是缓存同一个文件, 也可能由于缓存的版本不一致, 而造成各个页面资源不一致.
manifest缓存规则
- 遵循全量缓存的规律. 即: manifest文件改动后, 将重新缓存一遍所有的文件(包括html本身和动态添加的需要缓存的文件,即使缓存列表中没有该html). 第一次缓存过程中如果出现缓存失败的文件, 那么, 第二访问, 又将重新缓存一遍所有的文件. 以此类推.
- manifest文件本身不能写进缓存清单, 否则连同html和资源在其缓存失效之前, 将永远不能获得更新.
- 即使manifest文件丢失, 原缓存依然有效. 如果不删除html节点的manifest属性, 引入该manifest的html, 将永远不能获得更新. 因此正确的清除缓存姿势为:删除manifest文件,并且删除html节点的manifest属性,从而彻底解除绑定(此后页面在第二次加载时就会获取最新内容).
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]
如何更新缓存
- 更新manifest文件后, webview将自动更新缓存.
- js更新缓存(手动触发manifest更新): window.applicationCache.update();
其他
chrome浏览器下通过访问 chrome://appcache-internals/ 可以查看缓存在本地的资源文件.
另外, 除了本文参考的一篇 MDN 的文章以及 HTML5 Rocks的 Beginner’s Guide to Using the Application Cache 一文, 还有如下三个链接可供您比较阅读, 谢谢.
- Cache manifest in HTML5 on Wikipedia
- Offline Web Applications W3C Working Group Note
- Offline Web applications at WHATWG
本问就讨论这么多内容,大家有什么问题或好的想法欢迎在下方参与留言和评论.
本文作者: louis
本文链接: http://louiszhai.github.io/2016/11/25/manifest/
参考文章