HTML5之离线缓存
字数 2.4k / 8 min 读完 / 阅读乐刻教练端业务是一个 Hybrid APP,通过原生 APP 提供调用原生能力的外壳,内部逻辑全部使用 H5 页面实现。 这种方式在早期产品需求快速迭代,客户端资源紧张的情况下,解决了很大一部分问题。但随着业务需要和使用人数的增加,教练端已经有点力不从心的。在 3 月份,已经对教练端进行过一次优化,主要集中在资源加载、初始数据、转场动画等。接下来,想要结合 HTML5 离线缓存机制,进行一些优化尝试。
HTML5 离线缓存
是什么
HTML5 的新增了很多浏览器本地存储的技术,Application Cache(简称 AppCache)使得基于 web 的应用程序可以离线运行,似乎是为支持 Web App 离线使用而开发的缓存机制。它的缓存机制类似于浏览器的缓存(Cache-Control 和 Last-Modified)机制,都是以文件为单位进行缓存,且文件有一定更新机制。但 AppCache 是对浏览器缓存机制的补充,不是替代。
有什么用
- 离线浏览: 用户可以在离线状态下浏览网站内容。
- 更快的速度: 因为数据被存储在本地,所以速度会更快。
- 减轻服务器的负载: 浏览器只会下载在服务器上发生改变的资源。
怎么用
通过在站点根目录维护一个 manifest 文件,在需要缓存的 html 页面中引入这个文件。manifest 相当于一个配置文件,配置需要缓存的文件等规则,下面会详细列出。
假如在根目录下新建一个名为 manifest.appcache 的文件(该文件名和后缀名可以自定义),配置内容如下:
CACHE MANIFEST
# version 1.0.0 以 # 声明注释,浏览器根据manifest文件变化来检测是否需要重新加载新文件。
# 注释:需要缓存的文件,无论在线与否,均从缓存里读取
/images/cached.png
/css/cached.css
/js/cached.js
# 需要特殊声明的缓存文件,也可以都在这里声明
CACHE:
/css/otherCached.css
/js/otherCached.js
# 注释:不缓存的文件,无论缓存中存在与否,均从新获取(如果与上面的缓存内容重复,依旧会缓存)
NETWORK:
*
# 注释:获取不到资源时的备选路径,如index.html访问失败,则返回404页面
FALLBACK:
index.html 404.html
1、CACHE MANIFEST — 文件开头第一行必须声明 CACHE MANIFEST 字段标识,然后紧接着声明需要缓存的文件路径,作用是标识出哪些文件需要缓存,可以是相对路径也可以是绝对路径;
2、# — #号开头的是注释,一般会在第二行写个版本号,用来在缓存的文件更新时,更改manifest的作用,可以是版本号,时间戳或者md5码等等;
3、CACHE — 我们也可以在CACHE下面声明需要缓存的资源路径;
4、NETWORK可选,这一部分是要直接读取的文件,可以使用通配符 * ;
5、FALLBACK可选,指定了一个后备页面,当资源无法访问时,浏览器会使用该页面。
接下来只需要在 html 文件上,引入该配置文件即可
在文档的 <html>
标签中添加 manifest 属性,将 manifest.appcache 文件引入:
<html manifest="manifest.appcache">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"
/>
<meta name="format-detection" content="telephone=no" />
<title>乐刻教练</title>
</head>
<body></body>
</html>
这样浏览器在读取 HTML 页面时会将 manifest.appcache 中声明的资源下载并进行离线存储,下次请求直接使用离线存储中的资源,HTTP 请求表现为:200 (from disk cache)
注意:
一般情况下,manifest 文件需要在 web 服务器上配置正确的 MIME-type,即 “text/cache-manifest”,才能正常的被浏览器正常请求。
- webpack 自动化配置
在使用 webpack 自动化构建的项目中,我们可以利用appcache-webpack-plugin这个插件轻松生成 manifest 文件,并将打包的资源路径更新到 manifest 文件中,配置如下
const AppCachePlugin = require("appcache-webpack-plugin");
plugins: [
new AppCachePlugin({
cache: ["otherAsset.png"],
network: ["*"], // 除了声明的缓存资源其他都通过网络访问
fallback: ["index.html", "error.html"],
settings: ["prefer-online"],
exclude: ["file.txt", /.*\.html$/], // Exclude file.txt and all .html files
output: "/manifest.appcache",
}),
];
什么效果
浏览器离线存储缓存机制示意图:
- 用户在初次访问网站时,浏览器读取到 HTML 上的 manifest 属性声明的 mainfest.appcache 文件,根据文件内的配置项下载相应的资源进行离线缓存。在 chrome 中可以看到,被缓存的资源。(浏览器在下载 manifest 中的文件时,会一次性下载所有的文件,一旦因为某些原因某一个资源下载失败,会导致所有的资源更新失败,浏览器继续适应旧的资源。)
已经缓存离线存储后,用户再次访问网站,浏览器(无论在线与否)将会使用已缓存的资源加载页面,然后浏览器会对比新的 manifest 文件,如果文件发生变化,就重新下载文件中的资源进行离线,如果没有变化,就不做改变。
注意:
- 站点中的其他页面即使没有设置 manifest 属性,请求的资源如果在缓存中也从缓存中访问。
- 浏览器下载完新的更新资源后并不会立即使用,需要刷新页面后才有效果,解决办法:
var appCache = window.applicationCache;
appCache.onupdateready = function (e) {
appCache.swapCache();
window.location.reload();
};
离线情况下,浏览器直接访问离线资源
与传统浏览器缓存的区别
- 离线缓存是针对整个应用,浏览器缓存是单个文件。
- 离线缓存断网了还是可以打开页面,浏览器缓存不行。
- 离线缓存可以通过代码主动通知浏览器更新资源。
chrome 浏览器下通过访问 chrome://appcache-internals/ 可以查看缓存在本地的资源文件.
API 介绍
applicationCache 对象
该对象是 window 对象的直接子对象 window.applicationCache
基类:DOMApplicationCache
事件列表
事件 | 接口 | 触发条件 | 后续事件 |
---|---|---|---|
checking | Event | 用户代理检查更新或者在第一次尝试下载 manifest 文件的时候,本事件往往是事件队列中第一个被触发的 | noupdate, downloading, obsolete, error |
noupdate | Event | 检测出 manifest 文件没有更新 | 无 |
downloading | Event | 用户代理发现更新并且正在取资源,或者第一次下载 manifest 文件列表中列举的资源 | progress, error, cached, updateready |
progress | ProgressEvent | 用户代理正在下载资源 manifest 文件中的需要缓存的资源 | progress, error, cached, updateready |
cached | Event | manifest 中列举的资源已经下载完成,并且已经缓存 | 无 |
updateready | Event | manifest 中列举的文件已经重新下载并更新成功,接下来 js 可以使用 swapCache()方法更新到应用程序中 | 无 |
obsolete | Event | manifest 的请求出现 404 或者 410 错误,应用程序缓存被取消 | 无 |
另外还有一个 error 事件,触发条件如下:
- manifest 的请求出现 404 或者 410 错误,更新缓存的请求失败;
- 无 manifest 文件没有改变,但是页面引用的 manifest 文件没有被正确地下载;
- 在取 manifest 列举的资源的过程中发生致命的错误;
- 在更新过程中 manifest 文件发生变化用户代理会尝试立即再次获取文件。
属性
status 属性返回缓存的状态
可选值 | 匹配常量 | 描述 |
---|---|---|
0 | appCache.UNCACHED | 未缓存 |
1 | appCache.IDLE | 闲置 |
2 | appCache.CHECKING | 检查中 |
3 | appCache.DOWNLOADING | 下载中 |
4 | appCache.UPDATEREADY | 已更新 |
5 | appCache.OBSOLETE | 失效 |
方法
方法名 | 描述 |
---|---|
update() | 发起应用程序缓存下载进程 |
abort() | 取消正在进行的缓存下载 |
swapcache() | 切换成本地最新的缓存环境 |
支持程度
不过在 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 文件改变后,应用还是不能及时更新(对于业务代码不使用 manifest 缓存,只缓存一些类库文件和不经常改变的文件)
总结
- 在教练端 APP 上加载速率上没有明显变化,且在断网情况下 APP 有默认缺省图(在 chrome 浏览器上,加载速率提升明显)
- H5 离线缓存机制 Manifest 将被废除,由 PWA 技术取代
- 网上有讨论:会出现更新代码后,页面无法更新问题(没有复现)
- 结合上述现象,决定不再继续深究此方案