H5 和移动端 WebView 缓存机制解析与实战

阅读:437 2019-03-19 14:43:25 来源:新网

在web项目开发中,我们可能都曾碰到过这样一个棘手的问题:

线上项目需要更新一个有问题的资源(可能是图片,js,css,json数据等),这个资源已经发布了很长一段时间,为什么页面在浏览器里打开还是没有看到更新?

有些web开发经验的同学应该马上会想到,可能是资源发布出了岔子导致没有实际发布成功,更大的可能是老的资源被缓存了。说到web缓存,首先我们要弄清它是什么。web缓存可以理解为web资源在web服务器和客户端(浏览器)的副本,其作用体现在减少网络带宽消耗、降低服务器压力和减少网络延迟,加快页面打开速度等方面(笔者在香港求学期间看到港台地区将cache译为“快取”,除了读音相近,大概就是贴近这层含义)。他们通常还会告诉你:ctrl+f5强刷一下,但是本文下面的内容将会说明为什么强制刷新在去除缓存上不总是能奏效的,更何况对于线上项目而言,总不能让所有已经访问过的用户撸起袖子岔开两个手指都强制刷新一下吧?

同时,当前原生+html5的混合模式移动应用(hybridapp)因可大幅降低移动应用的开发成本,并且可在用户桌面形成独立入口以及有接近原生应用的体验而大行其道,app内嵌h5应用的开发也是本人现在工作内容重要的一部分,本文将从实际项目开发中遇到的问题出发,一窥html5和app内webview的缓存机制真容。

回到开头的那个问题,更新了一张图片,发布之后反复重新进页面总是看不到更新,这是为什么呢?

这里我们假设已经排除了资源没有发布成功过的情况,那么第一步,我们可能会认为是http协议缓存(也称为浏览器缓存或者网页缓存)。

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通常与last-modified一起使用。一个用于控制缓存有效时间,一个在缓存失效后,向服务查询是否有更新。

cache-control还有一个同功能的字段:expires。expires的值一个绝对的时间点,如:expires:thu,10nov201508:45:11gmt,表示在这个时间点之前,缓存都是有效的。

expires是http1.0标准中的字段,cache-control是http1.1标准中新加的字段,功能一样,都是控制缓存的有效时间。当这两个字段同时出现时,cache-control是高优化级的。

etag也是和last-modified一样,对文件进行标识的字段。不同的是,etag的取值是一个对文件进行标识的特征字串。在向服务器查询文件是否有更新时,浏览器通过if-none-match字段把特征字串发送给服务器,由服务器和文件最新特征字串进行匹配,来判断文件是否有更新。没有更新回包304,有更新回包200。etag和last-modified可根据需求使用一个或两个同时使用。两个同时使用时,只要满足基中一个条件,就认为文件没有更新。

一个比较形象的理解:

翠花:狗蛋,你几岁了?狗蛋:我18岁了。(200)翠花记住了狗蛋18岁(200fromcache)

=================================

翠花:狗蛋,你几岁了?我猜你18岁。狗蛋:靠,知道还问我!(304)

=================================

翠花:狗蛋,你几岁了?我猜你18岁。狗蛋:翠花,我已经19岁了。(200)

不过有两种情况比较特殊:

手动刷新页面(f5),浏览器会直接认为缓存已经过期(可能缓存还没有过期),在请求中加上字段:cache-control:max-age=0,发包向服务器查询是否有文件是否有更新。

强制刷新页面(ctrl+f5),浏览器会直接忽略本地的缓存(有缓存也会认为本地没有缓存),在请求中加上字段:cache-control:no-cache(或pragma:no-cache),发包向服务重新拉取文件。

当然,各个浏览器对于刷新和强制刷新的实现方式也有一些区别。

那么,如果线上更新了web资源,如何能让尽快更新呢?(要知道像图片这样比较少更新的资源一般缓存时间都设置得比较长,比如game.gtimg.cn域名下是一天,有问题的图片在用户侧缓存这么长时间是不可接受的)

方法一修改请求header头,比如php添加:

header("expires:mon,26jul199705:00:00gmt");header("cache-control:no-cache,must-revalidate");header("pragma:no-cache");

方法二修改html的head块:

方法三:添加随机参数:

对于图片或者css,可使用如下方式:

对于js则可以直接使用时间戳:

二、应用缓存

除了http协议缓存,html5提供一种应用程序缓存机制,使得基于web的应用程序可以离线运行。为了能够让用户在离线状态下继续访问web应用,开发者需要提供一个cachemanifest文件。这个文件中列出了所有需要在离线状态下使用的资源,浏览器会把这些资源缓存到本地。例如以下页面:

calender

thetimeis:

其对应的calender.manifest代码

cachemanifestcalender.htmlcalender.csscalender.js

cachemanifest格式遵循以下原则:

例如以下manifest文件:

cachemanifest#上一行是必须书写images/sound-icon.pngimages/background.pngnetwork:comm.cgi#下面是另一些需要缓存的资源,在这个示例中只有一个css文件。cache:style/default.cssfallback:/files/projects/projects

那么,如果使用了应用缓存,应该如何去更新呢?有以下两种方式

浏览器除了在第一次访问web应用时缓存资源外,只会在cachemanifest文件本身发生变化时更新缓存。而cachemanifest中的资源文件发生变化并不会触发更新。

开发者也可以使用window.applicationcache的接口更新缓存。方法是检测window.applicationcache.status的值,如果是updateready,那么可以调用window.applicationcache.update()更新缓存。示范代码如下。

手动更新缓存代码:

if(window.applicationcache.status==window.applicationcache.updateready){window.applicationcache.update();}

**然而,有时候虽然应用缓存刷新了,但是还是不能看到最新的:那么有可能是使用了本地存储。**常用的本地存储有domstorage和websql和indexdb三种

,细节可以参考这篇文章《html5storagewars-localstoragevs.indexeddbvs.websql》,这里就不展开了,需要注意的是,若使用本地存储,想要清理缓存,除了清理本地存储文件外,还需要重启app,以消除内存中的备份。

至此,一个完成的流程图就出来了:

笔者现在常会和移动端app内嵌html5页面打交道,那么移动端hybrid方式开发的app,如何支持以上的缓存方式呢?

需要了解这些,我们先了解下hybrid方式开发的app怎么展示网页。简单得说就是使用了webview,那么什么是webview呢?webview是手机中内置了一款高性能webkit内核浏览器,在sdk中封装的一个组件。没有提供地址栏和导航栏,webview只是单纯的展示一个网页界面。简单地可以理解为简略版的浏览器。

在data/应用package下生成database与cache两个文件夹,请求的url记录是保存在webviewcache.db里,而url的内容是保存在webviewcache文件夹下。

<1>缓存构成

/data/data/package_name/cache//data/data/package_name/database/webview.db/data/data/package_name/database/webviewcache.db

<2>缓存模式

如果一个页面的cache-control为no-cache,在模式load_default下,无论如何都会从网络上取数据,如果没有网络,就会出现错误页面;在load_cache_else_network模式下,无论是否有网络,只要本地有缓存,都使用缓存。本地没有缓存时才从网络上获取。如果一个页面的cache-control为max-age=60,在两种模式下都使用本地缓存数据。

根据setappcachepath(stringappcachepath)提供的路径,在h5使用缓存过程中生成的缓存文件。

无模式选择,通过setappcacheenabled(booleanflag)设置是否打开。默认关闭,即,h5的缓存无法使用。如果要手动清理缓存,需要找到调用setappcachepath(stringappcachepath)设置缓存的路径,把它下面的文件全部删除就ok了。

ios的uiwebview组件不支持html5应用程序缓存的方式,对于协议缓存,可以使用sdk中的nsurlcache类。nsurlrequest需要一个缓存参数来说明它请求的url何如缓存数据的,我们先看下它的cachepolicy类型。

处于数据安全性的考虑,ios的应用拥有自己独立的目录,用来写入应用的数据或者首选项参数。应用安装后,会有对应的home目录,基于nsurlcache来实现数据的cache,nsurlcache会存放在home内的子目录library/caches下,以bundleidentifier为文件夹名建立cache的存放路径。在xcode下可以管理对应的文件,具体可以参见此文:《关于ios删除缓存的那些事儿》

综上所述,html5缓存主要可以分为http协议缓存、应用缓存、domstorage、websql和indexeddb几种方式,针对不同的方式清理缓存的方式也不尽相同,上文中都有说明。同时,在移动端webview层,对html缓存机制做了支持(从笔者接触过的手游和相关app来看,目前使用默认缓存机制的比较多),项目开发过程中缓存更新和清理方式也需要有针对性地选择使用。

相关文章
{{ v.title }}
{{ v.description||(cleanHtml(v.content)).substr(0,100)+'···' }}
你可能感兴趣
推荐阅读 更多>
推荐商标

{{ v.name }}

{{ v.cls }}类

立即购买 联系客服