亲,如果你还在为你没网打开不网页而烦恼吗?
亲,你还在为你web服务器复杂的配置项而蛋疼吗?
不要998,manifest抱回家~
manifest自H5横空出世以来给前端网页的浏览带来了翻天覆地的变化,以前我们的网页必须在有网的前提下打开(主要还是打开HTML), 但是现在,我们可以offline 浏览。 可以算是实现web app的一个特技。
manifest的兼容性 IE9+. 由于是现代的技术,IE9以下的古老浏览器是不支持的。所以,manifest主要应用是针对现代浏览器或者手机端更多一些。
入门manifest
浏览器检测你是否使用manifest特技时,是检测html标签.
<html lang="en" manifest="usable.manifest">
当解析你的HTML时,发现存在manifest文件时,则会进行如下的操作:
(from alloy team)
manifest文件可以是任意后缀比如. usable.manifest||usable.mf等,但是他的MIMEtype必须设置正确.
记住,这个时候manifest会将HTML文件也一并保存,这需要注意。
书写manifest文件
一个简单的demo:
CACHE MANIFEST
#version 1.3
/public/static/index.css
/public/static/header.css
NETWORK:
*
FALLBACK:
/userInfo/ /404.html
#额外需要添加的缓存文件
CACHE:
images/logo1.png
images/logo2.png
基本样式就是上述
CACHE MANIFEST/CACHE
第一行必须是指定头即, "CACHE MANIFEST"(不能有其他的). 表示哪些文件需要缓存。如果是相对路径则是,在manifest文件所在的目录下。而且,不能使通配符!!!(tm 你是还不是傻). 所以一般而言只能一个一个配置.
CACHE MANIFEST
#相对于manifest文件所在的目录
./index.css
注释: 注释使用#+"info"
可以对缓存文件性质进行适当的说明。缓存后的文件,就会被带上Expires的头,表示可以不经过服务器验证直接使用本地文件。所以,返回status Code 为 200.
另外,CACHE 定义的文件内容,和CACHE MANIFEST 是一个效果,只是跟在CACHE MANIFEST之后,就可以省略书写CACHE,你添加上也可以。
CACHE MANIFEST
#version 1.3
CACHE:
/favicon.ico
而且CACHE可以放在文中的任意位置,不过一般都是放开头,或者省略.
CACHE MANIFEST
# 缓存文件
index.html
css/style.css
NETWORK:
*
# 额外的需要缓存的文件
CACHE:
images/logo1.png
images/logo2.png
images/logo3.png
NETWORK
这里设置不使用缓存的文件,可以使用通配符"*"等。
* 表示,除了CACHE MANIFEST定义的文件之外的文件都不能被缓存。
当然也可以手动指定文件:
NETWORK
*
http://www.example.com/index.html
http://www.example.com/header.png
http://www.example.com/blah/blah
这些浏览器都不能直接使用缓存,即,可能会要求你重新验证,或者直接使用服务器文件。
FALLBACK
这个tag,可用可不用。 用来表示,指定文件无法加载时,使用另外的文件代替。参数有两部分构成,第一部分是指定资源(可能存在文件未加载),第二部分是替代资源
FALLBACK:
/index.html /404.html
/static/* /404.html
/images/* /NotFound.jpg
当index.html无法加载时,使用404.html代替. 这里有个要求,两个路径必须使用相对路径并且与清单文件同源。
SETTINGS
这算是一个附加属性吧。通常设置内容就只有:
SETTINGS:
prefer-online
表示,在有网的情况下,会先访问服务器的文件,看有没有更新,相当于设置了Cache-Control:max-age=0,must-revalidate; + ETag||Last-modified. 不过,比较stupid的是,只有FF(Opera 12)支持.
服务器设置manifest
而在服务器端,需要对manifest文件的MIME设置正确。这里以nginx为例, 具体设置一下MIME type
type{
image/gif gif;
image/jpeg jpeg jpg;
application/x-javascript js;
}
详情可以参考: manifest文件配置
自动生成manifest文件配置
这里以gulp为例。 可以在npm里面很容易找到gulp-manifest这个生成插件.
直接下载:
npm install gulp-manifest --save-dev
然后在gulpfile里面配置:
gulp.task('manifest', function(){
gulp.src(['build/**'], { base: './' })
.pipe(manifest({
hash: true,
preferOnline: true,
network: ['*'],
fallback:['/images/* /404.html']
filename: 'app.manifest',
exclude: 'app.manifest' //不保存manifest,不过有没有效果一样
}))
.pipe(gulp.dest('./'));
});
接着就会在目录下生成app.manifest文件,里面就是一些基本的文件格式了。另外如果你想查看你电脑有多少网页是manifest,可以直接访问 chrome://appcache-internals/.
manifest的坑点
manifest对于单页应用可谓是如鱼得水,但是,到了多页应用的层面,他的bug真的是暴露无遗。
1.页面保存的复杂度,
2.文件的及时更新,
3.缓存文件的设置,
4.死都会保存HTML,
5.文件下载出错,则这次更新缓存失败,
6.覆盖所有缓存头,除了Cache-Control:no-store
7.在Android 4.4的webview里,关闭之后会丢失cache
8.IE10不能很好的支持FALLBACK部分.
所以,appCache的bug也是非常多的。
例如,长尾更新问题,当你的页面保持在线的时候,是无法检测文件已经更新,除非你reload页面,但是用户并不知道你已经更新,所以这里我们需要引进js的提供的缓存检测API.
window.applicationCache
这是前端能够摸到缓存最真实的API。我们可以通过这个API接口获取到我们很多想要的东西:
var appcache = window.applicationCache;
console.log(appcache.status); //检查当前缓存状态
console.log(appcache.IDLE); //缓存状态常量,下面解释
常用的属性有:
属性名 | explanation |
---|---|
status | 当前缓存状态,为Number类型. 为0~5 |
UNCACHED(0) | 浏览器未缓存文件 |
IDLE(1) | 空闲状态,浏览器已经全部缓存 |
CHECKING(2) | 页面正在检查当前离线缓存是否需要更新 |
DOWNLOADING(3) | 页面正在下载需要更新的缓存文件 |
UPDATEREADY(4) | 页面缓存更新完毕 |
OBSOLETE(5) | 缓存已经过期 |
常用的方法:
window.applicationCache.update()
//update方法调用时,页面会主动与服务器通信,检查页面当前的缓存是否为最新的,如不是,则下载更新后的资源
window.applicationCache.swapCache() //updateready后,更新到最新的应用缓存
通常结合上述两个方法和相应的属性我们可以手动触发文件的更新(前提是 manifest文件改动).
var appCache = window.applicationCache;
appCache.update();
//检查更新
if (appCache.status == window.applicationCache.UPDATEREADY) {
//如果存在更新,并且已经下载ok,则替换浏览器缓存
appCache.swapCache();
}
但是,此时页面并不能用上最新的文件,只是浏览器的缓存已经改变,网页实际内容还是原来的内容,还需要手动进行reload,才能进行更新文件
window.addEventListener('load', function(e) {
window.applicationCache.addEventListener('updateready', function(e) {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
if (confirm('文件有更新,手否重新加载文件')) {
window.location.reload();
}
} else {
//如果,拒绝则不刷新网页
}
}, false);
}, false);
cache相关事件
相关事件有: checking,downloading,updateready,obsolete,cached,error,noupdate,progress.
对照上述的status就可以很容易知道每个事件对应的效果是神马。 需要说的就是:
progress: 当浏览器在下载资源时,每下载成功一次,就会触发一次
noupdate:当浏览器检查更新之后发现没有资源更新的时候触发这个事件
error: 更新出错时会触发,比如文件无法正常下载,manifest文件被删除.
其实,使用manifest的时候,无外乎就是3种常用状态
第一次访问页面时
再次访问页面时,没有更新
再次访问页面时,有更新
每次,触发的事件顺序为:
行为 | 事件顺序 |
---|---|
第一次访问页面 | checking->downloading->progress(多次)->cached |
再次访问时,没有更新 | checking->noupdate |
再次访问时,有更新 | checking->downloading->progress(多次)->updateready |
上面看不懂没关系,我们可以看看更直观的Console的内容。
第一次访问页面时
checking->downloading->progress(多次)->cache
2\. 再次访问页面时,没有更新
checking->noupdate
3\. 再次访问页面时,有更新
checking->downloading->progress(多次)->updateready
浅谈manifest
其实,manifest就是为了离线应用而生的,但是由于设计之初,没有很好的规范,导致现在manifest的bug,真的超级多。
看到whatwg上面说的一句话,真的更加蛋疼.
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.
意思就是让你不要用manifest,应该他迟早要被fire的,但是,这一天还有很多年,很多年。 另外一个替代方案就是使用SS,但是兼容性,真的极其差。几乎现在的浏览器都没有实现(除了布道师FF实现了部分). 现在我们真的很尴尬,不过,目前的情况而言,in my opinion, 是十分推荐使用的(也没有其他的办法了). 那该怎么做,才能将manifest的Bug减到最低呢?
推荐的做法是将逻辑页面和用户数据给分离开。 逻辑页面使用app cache,而用户数据可以保存在web Storage || indexDB 等浏览器数据库里,动态更新data时,使用web Socket,ajax,SSE等技术.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。