NoSQLH5 缓存机制浅析 移动端 Web 加载品质优化

腾讯Bugly特约小编:贺辉超

1 H5 缓存机制介绍

H5,即 HTML5,是新一代的 HTML
标准,加入过多新的特色。离线存储(也可称之为缓存机制)是里面一个老大重大的表征。H5
引入的离线存储,那表示 web
应用可举办缓存,并可在尚未因特网连接时开展走访。

H5 应用程序缓存为利用带来两个优势:

  • 离线浏览 用户可在选取离线时选取它们
  • 进程 已缓存资源加载得更快
  • 削减服务器负载 浏览器将只从服务器下载更新过或变更过的资源。

    依据标准,到最近截至,H5 一共有6种缓存机制,有些是事先已有,有些是
    H5 才新参加的。

  1. 浏览器缓存机制
  2. Dom Storgage(Web Storage)存储机制
  3. Web SQL Database 存储机制
  4. Application Cache(AppCache)机制
  5. Indexed Database (IndexedDB)
  6. File System API

    上面大家先是分析各类缓存机制的法则、用法及特色;然后针对 Anroid
    移动端 Web 品质加载优化的须要,看即使使用恰当缓存机制来增长 Web
    的加载质量。


2 H5 缓存机制原理分析

2.1 浏览器缓存机制

浏览器缓存机制是指通过 HTTP 协议头里的 Cache-Control(或 Expires)和
Last-Modified(或 Etag)等字段来决定文件缓存的机制。这应该是 WEB
中最早的缓存机制了,是在 HTTP 协议中达成的,有点不一样于 Dom
Storage、AppCache
等缓存机制,但真相上是均等的。可以清楚为,一个是协商层完成的,一个是应用层完成的。

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, 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),发包向服务重新拉取文件。

    上边是通过 谷歌 Chrome
    浏览器(用其余浏览器+抓包工具也得以)自带的开发者工具,对一个资源文件不一样意况请求与回包的截图。

    首次呼吁:200

    NoSQL 1

    缓存有效期内呼吁:200(from cache)

    NoSQL 2

    缓存过期后呼吁:304(Not Modified)

    NoSQL 3

    貌似浏览器会将缓存记录及缓存文件存在本地 Cache 文件夹中。Android 下
    App 倘诺采纳 Webview,缓存的公文记录及文件内容会存在当前 app 的 data
    目录中。

    解析:Cache-Control 和 Last-Modified 一般用在 Web
    的静态资源文件上,如 JS、CSS
    和有些图像文件。通过设置资源文件缓存属性,对进步资源文件加载速度,节省流量很有含义,越发是移动网络环境。但难题是:缓存有效时长该怎样设置?即使设置太短,就起不到缓存的行使;假诺设置的太长,在资源文件有更新时,浏览器假使有缓存,则无法立时取到最新的文书。

    Last-Modified
    须要向服务器发起查询请求,才能领悟资源文件有没有立异。纵然服务器可能回到304告诉没有更新,但也还有一个呼吁的长河。对于运动互连网,这么些请求可能是相比耗时的。有一种说法叫“消灭304”,指的就是优化掉304的伏乞。

    抓包发现,带 if-Modified-Since
    字段的伸手,若是服务器回包304,回包带有 Cache-Control:max-age 或
    Expires
    字段,文件的缓存有效时间会更新,就是文本的缓存会重新有效。304回包后假若再请求,则又径直运用缓存文件了,不再向服务器查询文件是还是不是更新了,除非新的缓存时间另行过期。

    其余,Cache-Control 与 Last-Modified
    是浏览器内核的机制,一般都是标准的落成,不可能更改或设置。以 QQ
    浏览器的 X5为例,Cache-Control 与 Last-Modified
    缓存不能够禁用。缓存容量是12MB,不分HOST,过期的缓存会开端被排除。尽管都没过期,应该事先清最早的缓存或最快到期的或文件大小最大的;过期缓存也有可能依旧管用的,清除缓存会造成资源文件的重新拉取。

    还有,浏览器,如
    X5,在应用缓存文件时,是未曾对缓存文件内容开展校验的,这样缓存文件内容被修改的也许。

    解析发现,浏览器的缓存机制还不是万分周详的缓存机制。完美的缓存机制应该是这么的:

  1. 缓存文件没更新,尽可能选择缓存,不用和服务器交互;
  2. 缓存文件有更新时,第一时间能利用到新的文书;
  3. 缓存的文件要保持完整性,不行使被涂改过的缓存文件;
  4. 缓存的容量大小要能设置或决定,缓存文件无法因为存储空间范围或逾期被扫除。
    以X5为例,第1、2条不可以同时满意,第3、4条都不可以知足。

    在事实上使用中,为通晓决 Cache-Control
    缓存时长不佳设置的标题,以及为了”消灭304“,Web前端应用的法门是:

  5. 在要缓存的资源文件名中丰裕版本号或文件 MD5值字串,如
    common.d5d02a02.js,common.v1.js,同时设置
    Cache-Control:max-age=31536000,也就是一年。在一年时光内,资源文件假设地点有缓存,就会采取缓存;也就不会有304的回包。

  6. 借使资源文件有涂改,则更新文件内容,同时修改资源文件名,如
    common.v2.js,html页面也会引用新的资源文件名。

    经过那种艺术,落成了:缓存文件没有更新,则利用缓存;缓存文件有立异,则第一时间使用新型文件的目的。即上面说的第1、2条。第3、4条由于浏览器内部机制,方今还不能满意。

    #### 2.2 Dom Storage 存储机制

    DOM 存储是一套在 Web Applications 1.0
    规范中第一次引入的与仓储相关的特点的总称,现在一度分离出来,单独发展变成独立的
    W3C Web 存储规范。 DOM
    存储被规划为用来提供一个更大存储量、更安全、更省事的存储方法,从而可以代表掉将有些不需求让服务器知道的音讯囤积到
    cookies 里的这种传统艺术。

    地点一段是对 Dom Storage 存储机制的官方公布。看起来,Dom Storage
    机制就像 Cookies,但有一些优势。

    Dom Storage 是经过存储字符串的 Key/Value 对来提供的,并提供 5MB
    (不相同浏览器可能两样,分 HOST)的储存空间(库克ies 才 4KB)。别的 Dom
    Storage 存储的数目在本土,不像 Cookies,每一遍请求一回页面,Cookies
    都会发送给服务器。

    DOM Storage 分为 sessionStorage 和 localStorage。localStorage 对象和
    sessionStorage
    对象使用方法基本相同,它们的差异在于作用的限定分裂。sessionStorage
    用来存储与页面相关的多少,它在页面关闭后不可能使用。而 localStorage
    则持久存在,在页面关闭后也足以应用。

    Dom Storage 提供了以下的积存接口:

    interface Storage {
    readonly attribute unsigned long length;
    [IndexGetter] DOMString key(in unsigned long index);
    [NameGetter] DOMString getItem(in DOMString key);
    [NameSetter] void setItem(in DOMString key, in DOMString data);
    [NameDeleter] void removeItem(in DOMString key);
    void clear();
    };
    

    sessionStorage 是个全局对象,它爱慕着在页面会话(page
    session)时期有效的积存空间。只要浏览器开着,页面会话周期就会一贯不绝于耳。当页面重新载入(reload)或者被復苏(restores)时,页面会话也是平昔留存的。每在新标签或者新窗口中开辟一个新页面,都会伊始化一个新的对话。

    <script type="text/javascript">
     // 当页面刷新时,从sessionStorage恢复之前输入的内容
     window.onload = function(){
        if (window.sessionStorage) {
            var name = window.sessionStorage.getItem("name");
            if (name != "" || name != null){
                document.getElementById("name").value = name;
             }
         }
     };
    
     // 将数据保存到sessionStorage对象中
     function saveToStorage() {
        if (window.sessionStorage) {
            var name = document.getElementById("name").value;
            window.sessionStorage.setItem("name", name);
            window.location.href="session_storage.html";
         }
     }
     </script>
    
    <form action="./session_storage.html">
        <input type="text" name="name" id="name"/>
        <input type="button" value="Save" onclick="saveToStorage()"/>
    </form>
    

    当浏览器被意外刷新的时候,一些暂时数据应当被保存和复苏。sessionStorage
    对象在拍卖那种情况的时候是最可行的。比如復苏大家在表单中曾经填写的数额。

    把地点的代码复制到
    session_storage.html(也足以从附件中间接下载)页面中,用 谷歌(Google)Chrome 浏览器的分歧 PAGE 或 WINDOW
    打开,在输入框中分别输入不相同的文字,再点击“Save”,然后分别刷新。每个
    PAGE 或 WINDOW 呈现都是眼前PAGE输入的情节,互不影响。关闭
    PAGE,再另行打开,上五遍输入保存的内容早已远非了。

    NoSQL 4

    NoSQL 5

    Local Storage 的接口、用法与 Session Storage
    一样,唯一不相同的是:Local Storage 保存的多寡是持久性的。当前 PAGE
    关闭(Page Session
    停止后),保存的数量如故留存。重新打开PAGE,上次保留的数目可以赢得到。其它,Local
    Storage 是全局性的,同时打开多少个 PAGE
    会共享一份存多少,在一个PAGE中修改数据,另一个 PAGE
    中是足以感知到的。

    <script>
      //通过localStorage直接引用key, 另一种写法,等价于:
      //localStorage.getItem("pageLoadCount");
      //localStorage.setItem("pageLoadCount", value);
      if (!localStorage.pageLoadCount)
    localStorage.pageLoadCount = 0;
         localStorage.pageLoadCount = parseInt(localStorage.pageLoadCount) + 1;
         document.getElementById('count').textContent = localStorage.pageLoadCount;
    </script>
    
    <p>
        You have viewed this page
        an untold number of
        time(s).
    </p> 
    

    将方面代码复制到 local_storage.html
    的页面中,用浏览器打开,pageLoadCount 的值是1;关闭 PAGE
    重新打开,pageLoadCount 的值是2。那是因为第五次的值已经保存了。

    NoSQL 6

    NoSQL 7

    用五个 PAGE 同时打开 local_storage.html,并各自轮流刷新,发现五个PAGE 是共享一个 pageLoadCount 的。

    NoSQL 8

    NoSQL 9

    浅析:Dom Storage 给 Web
    提供了一种更录活的数额存储格局,存储空间更大(相对Cookies),用法也相比较简单,方便存储服务器或当地的一部分暂时数据。

    从 DomStorage 提供的接口来看,DomStorage
    适合储存相比较不难的多寡,如若要存储结构化的数据,可能要看重JASON了,将要存储的指标转为 JASON
    字串。不太适合储存相比较复杂或存储空间须求相比较大的数额,也不切合储存静态的文本等。

    在 Android 内嵌 Webview 中,必要通过 Webview 设置接口启用 Dom
    Storage。

    WebView myWebView = (WebView) findViewById(R.id.webview);
    WebSettings webSettings = myWebView.getSettings();
    webSettings.setDomStorageEnabled(true);
    

    拿 Android 类比的话,Web 的 Dom Storage 机制如同于 Android 的
    SharedPreference 机制。

    #### 2.3 Web SQL Database存储机制

    H5 也提供根据 SQL
    的数据库存储机制,用于存储适合数据库的结构化数据。依照官方的正经文档,Web
    SQL Database 存储机制不再推荐应用,将来也不再维护,而是推荐应用
    AppCache 和 IndexedDB。

    现行主流的浏览器(点击查阅浏览器接济情状)都如故支持 Web SQL
    Database 存储机制的。Web SQL Database 存储机制提供了一组 API 供 Web
    App 创造、存储、查询数据库。

    上边通过简单的例子,演示下 Web SQL Database 的使用。

    <script>
        if(window.openDatabase){
          //打开数据库,如果没有则创建
          var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024);
    
           //通过事务,创建一个表,并添加两条记录
          db.transaction(function (tx) {
               tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)');
               tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "foobar")');
               tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "logmsg")');
           });
    
          //查询表中所有记录,并展示出来
         db.transaction(function (tx) {
             tx.executeSql('SELECT * FROM LOGS', [], function (tx, results) {
                 var len = results.rows.length, i;
                 msg = "<p>Found rows: " + len + "</p>";
                 for(i=0; i<len; i++){
                     msg += "<p>" + results.rows.item(i).log + "</p>";
                 }
                 document.querySelector('#status').innerHTML =  msg;
                 }, null);
          });
    }
    
    </script>
    
    <div id="status" name="status">Status Message</div>
    

    将地方代码复制到 sql_database.html
    中,用浏览器打开,可观望上面的始末。

    NoSQL 10

    合法提出浏览器在促成时,对每个 HOST
    的数据库存储空间作一定限制,指出默许是 5MB(分
    HOST)的配额;达到上限后,可以申请更加多囤积空间。其余,现在主流浏览器
    SQL Database 的落到实处都是按照 SQLite。

    剖析:SQL Database
    的要害优势在于可以存储结构复杂的数码,能丰富利用数据库的优势,可方便对数据进行充实、删除、修改、查询。由于
    SQL 语法的错综复杂,使用起来麻烦一些。SQL Database
    也不太相符做静态文件的缓存。

    在 Android 内嵌 Webview 中,须求通过 Webview 设置接口启用 SQL
    Database,同时还要设置数据库文件的储存路径。

    WebView myWebView = (WebView) findViewById(R.id.webview);
    WebSettings webSettings = myWebView.getSettings();
    webSettings.setDatabaseEnabled(true);
    final String dbPath = getApplicationContext().getDir("db", Context.MODE_PRIVATE).getPath();
    webSettings.setDatabasePath(dbPath); 
    

    Android
    系统也接纳了汪洋的数据库用来储存数据,比如联系人、短新闻等;数据库的格式也
    SQLite。Android 也提供了 API 来操作 SQLite。Web SQL Database
    存储机制固然通过提供一组 API,借助浏览器的落到实处,将这种 Native
    的职能提须求了 Web App。

    #### 2.4 Application Cache 机制

    Application Cache(简称 AppCache)就如是为支撑 Web App
    离线使用而开发的缓存机制。它的缓存机制就好像于浏览器的缓存(Cache-Control

    Last-Modified)机制,都是以文件为单位开展缓存,且文件有早晚革新机制。但
    AppCache 是对浏览器缓存机制的补偿,不是代表。

    先拿 W3C 官方的一个例证,说下 AppCache 机制的用法与效果。

    <!DOCTYPE html>
    <html manifest="demo_html.appcache">
    <body>
    
    <script src="demo_time.js"></script>
    
    <p id="timePara"><button onclick="getDateTime()">Get Date and Time</button></p>
    <p><img src="img_logo.gif" width="336" height="69"></p>
    <p>Try opening <a href="tryhtml5_html_manifest.htm" target="_blank">this page</a>, then go offline, and reload the page. The script and the image should still work.</p>
    
    </body>
    </html>
    

    下面 HTML 文档,引用外部一个 JS 文件和一个 GIF 图片文件,在其 HTML
    头中通过 manifest 属性引用了一个 appcache 结尾的公文。

    大家在 谷歌 Chrome 浏览器中开辟这么些 HTML 链接,JS
    功效正常,图片也体现正常。禁用互连网,关闭浏览尊崇新打开那个链接,发现
    JS
    工作正常,图片也显得正常。当然也有可能是浏览缓存起的功用,大家能够在文书的浏览器缓存过期后,禁用互连网再试,发现
    HTML 页面也是正常的。

    通过 谷歌 Chrome 浏览器自带的工具,我们得以查看已经缓存的
    AppCache(分 HOST)。

    NoSQL 11

    上边截图中的缓存,就是我们刚刚打开 HTML 的页面
    AppCache。从截图中看,HTML 页面及 HTML 引用的 JS、GIF
    图像文件都被缓存了;其它 HTML 头中 manifest 属性引用的 appcache
    文件也缓存了。

    AppCache 的规律有四个关键点:manifest 属性和 manifest 文件。

    HTML 在头中通过 manifest 属性引用 manifest 文件。manifest
    文件,就是上面以 appcache
    结尾的公文,是一个常常文书文件,列出了亟待缓存的文件。

    NoSQL 12

    下面截图中的 manifest 文件,就 HTML 代码引用的 manifest
    文件。文件比较不难,第一行是非同一般字,第二、三行就是要缓存的文书路径(绝对路径)。那只是最简便易行的
    manifest 文件,完整的还包罗其它紧要字与内容。引用 manifest 文件的
    HTML 和 manifest 文件中列出的要缓存的文书最后都会被浏览器缓存。

    完全的 manifest 文件,包蕴四个 Section,类型 Windows 中 ini
    配置文件的 Section,不过并非中括号。

  7. CACHE MANIFEST – Files listed under this header will be cached after
    they are downloaded for the first time

  8. NETWORK – Files listed under this header require a connection to the
    server, and will never be cached

  9. FALLBACK – Files listed under this header specifies fallback pages
    if a page is inaccessible

    完整的 manifest 文件,如:

    CACHE MANIFEST
    # 2012-02-21 v1.0.0
    /theme.css
    /logo.gif
    /main.js
    
    NETWORK:
    login.asp
    
    FALLBACK:
    /html/ /offline.html 
    

    因此看来,浏览器在首次加载 HTML 文件时,会分析 manifest 属性,并读取
    manifest 文件,获取 Section:CACHE MANIFEST
    下要缓存的文书列表,再对文件缓存。

    AppCache
    的缓存文件,与浏览器的缓存文件分别储存的,仍旧一份?应该是分其余。因为
    AppCache 在地面也有 5MB(分 HOST)的上空限制。

    AppCache
    在首次加载生成后,也有更新机制。被缓存的文书倘诺要更新,要求更新
    manifest
    文件。因为浏览器在下次加载时,除了会默许使用缓存外,还会在后台检查
    manifest 文件有没有改动(byte by byte)。发现有改动,就会重新获得manifest 文件,对 Section:CACHE MANIFEST
    下文件列表检查更新。manifest
    文件与缓存文件的检讨更新也听从浏览器缓存机制。

    如用用户手动清了 AppCache
    缓存,下次加载时,浏览器会重新生成缓存,也可到头来一种缓存的换代。其它,
    Web App 也可用代码完毕缓存更新。

    剖析:AppCache
    看起来是一种相比好的缓存方法,除了缓存静态资源文件外,也适合创设 Web
    离线 App。在实际应用中微微必要注意的地点,有一些得以说是”坑“。

  10. 要翻新缓存的文书,要求更新包罗它的 manifest
    文件,那怕只加一个空格。常用的艺术,是修改 manifest
    文件注释中的版本号。如:# 2012-02-21 v1.0.0

  11. 被缓存的文件,浏览器是先使用,再经过检查 manifest
    文件是或不是有立异来更新缓存文件。那样缓存文件或者用的不是风靡的本子。

  12. 在更新缓存进度中,假使有一个文件更新败北,则全体更新会失利。
  13. manifest 和引用它的HTML要在同样 HOST。
  14. manifest 文件中的文件列表,假若是相对路径,则是相对 manifest
    文件的相对路径。
  15. manifest 也有可能更新出错,导致缓存文件更新失利。
  16. 没有缓存的资源在早就缓存的 HTML
    中不可以加载,即使有网络。例如:http://appcache-demo.s3-website-us-east-1.amazonaws.com/without-network/
  17. manifest 文件本身不能被缓存,且 manifest
    文件的翻新使用的是浏览器缓存机制。所以 manifest 文件的 Cache-Control
    缓存时间不可以设置太长。

    其余,按照官方文档,AppCache
    已经不推荐使用了,标准也不会再支撑。现在主流的浏览器都是还支持AppCache的,未来就不太确定了。

    在Android 内嵌 Webview中,要求经过 Webview 设置接口启用
    AppCache,同时还要设置缓存文件的仓储路径,其余还足以设置缓存的空间大小。

    WebView myWebView = (WebView) findViewById(R.id.webview);
    WebSettings webSettings = myWebView.getSettings();
    webSettings.setAppCacheEnabled(true);
    final String cachePath = getApplicationContext().getDir("cache", Context.MODE_PRIVATE).getPath();
    webSettings.setAppCachePath(cachePath);
    webSettings.setAppCacheMaxSize(5*1024*1024);
    

    #### 2.5 Indexed Database

    IndexedDB 也是一种数据库的囤积机制,但差别于已经不复协助的 Web SQL
    Database。IndexedDB 不是传统的关周密据库,可归为 NoSQL
    数据库。IndexedDB 又如同于 Dom Storage 的 key-value
    的蕴藏格局,但效益更强大,且存储空间更大。

    IndexedDB 存储数据是 key-value 的格局。Key 是不可或缺,且要唯一;Key
    能够友善定义,也可由系统自动生成。Value 也是要求的,但 Value
    格外灵活,可以是任何项目的目的。一般 Value 都是通过 Key 来存取的。

    IndexedDB 提供了一组 API,可以开展数据存、取以及遍历。那个 API
    都是异步的,操作的结果都是在回调中回到。

    下边代码演示了 IndexedDB 中 DB
    的开拓(创设)、存储对象(可清楚成有关周密据的”表“)的成立及数量存取、遍历基本作用。

    <script type="text/javascript">
    
    var db;
    
    window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
    
    //浏览器是否支持IndexedDB
    if (window.indexedDB) {
       //打开数据库,如果没有,则创建
       var openRequest = window.indexedDB.open("people_db", 1);
    
       //DB版本设置或升级时回调
       openRequest.onupgradeneeded = function(e) {
           console.log("Upgrading...");
    
           var thisDB = e.target.result;
           if(!thisDB.objectStoreNames.contains("people")) {
               console.log("Create Object Store: people.");
    
               //创建存储对象,类似于关系数据库的表
               thisDB.createObjectStore("people", { autoIncrement:true });
    
              //创建存储对象, 还创建索引
              //var objectStore = thisDB.createObjectStore("people",{ autoIncrement:true });
             // //first arg is name of index, second is the path (col);
            //objectStore.createIndex("name","name", {unique:false});
           //objectStore.createIndex("email","email", {unique:true});
         }
    }
    
    //DB成功打开回调
    openRequest.onsuccess = function(e) {
        console.log("Success!");
    
        //保存全局的数据库对象,后面会用到
        db = e.target.result;
    
       //绑定按钮点击事件
         document.querySelector("#addButton").addEventListener("click", addPerson, false);
    
        document.querySelector("#getButton").addEventListener("click", getPerson, false);
    
        document.querySelector("#getAllButton").addEventListener("click", getPeople, false);
    
        document.querySelector("#getByName").addEventListener("click", getPeopleByNameIndex1, false);
    }
    
      //DB打开失败回调
      openRequest.onerror = function(e) {
          console.log("Error");
          console.dir(e);
       }
    
    }else{
        alert('Sorry! Your browser doesn\'t support the IndexedDB.');
    }
    
    //添加一条记录
    function addPerson(e) {
        var name = document.querySelector("#name").value;
        var email = document.querySelector("#email").value;
    
        console.log("About to add "+name+"/"+email);
    
        var transaction = db.transaction(["people"],"readwrite");
    var store = transaction.objectStore("people");
    
       //Define a person
       var person = {
           name:name,
           email:email,
           created:new Date()
       }
    
       //Perform the add
       var request = store.add(person);
       //var request = store.put(person, 2);
    
       request.onerror = function(e) {
           console.log("Error",e.target.error.name);
           //some type of error handler
       }
    
       request.onsuccess = function(e) {
          console.log("Woot! Did it.");
       }
    }
    
    //通过KEY查询记录
    function getPerson(e) {
        var key = document.querySelector("#key").value;
        if(key === "" || isNaN(key)) return;
    
        var transaction = db.transaction(["people"],"readonly");
        var store = transaction.objectStore("people");
    
        var request = store.get(Number(key));
    
        request.onsuccess = function(e) {
            var result = e.target.result;
            console.dir(result);
            if(result) {
               var s = "<p><h2>Key "+key+"</h2></p>";
               for(var field in result) {
                   s+= field+"="+result[field]+"<br/>";
               }
               document.querySelector("#status").innerHTML = s;
             } else {
                document.querySelector("#status").innerHTML = "<h2>No match!</h2>";
             }
         }
    }
    
    //获取所有记录
    function getPeople(e) {
    
        var s = "";
    
         db.transaction(["people"], "readonly").objectStore("people").openCursor().onsuccess = function(e) {
            var cursor = e.target.result;
            if(cursor) {
                s += "<p><h2>Key "+cursor.key+"</h2></p>";
                for(var field in cursor.value) {
                    s+= field+"="+cursor.value[field]+"<br/>";
                }
                s+="</p>";
                cursor.continue();
             }
             document.querySelector("#status2").innerHTML = s;
         }
    }
    
    //通过索引查询记录
    function getPeopleByNameIndex(e)
    {
        var name = document.querySelector("#name1").value;
    
        var transaction = db.transaction(["people"],"readonly");
        var store = transaction.objectStore("people");
        var index = store.index("name");
    
        //name is some value
        var request = index.get(name);
    
        request.onsuccess = function(e) {
           var result = e.target.result;
           if(result) {
               var s = "<p><h2>Name "+name+"</h2><p>";
               for(var field in result) {
                   s+= field+"="+result[field]+"<br/>";
               }
               s+="</p>";
        } else {
            document.querySelector("#status3").innerHTML = "<h2>No match!</h2>";
         }
       }
    }
    
    //通过索引查询记录
    function getPeopleByNameIndex1(e)
    {
        var s = "";
    
        var name = document.querySelector("#name1").value;
    
        var transaction = db.transaction(["people"],"readonly");
        var store = transaction.objectStore("people");
        var index = store.index("name");
    
        //name is some value
        index.openCursor().onsuccess = function(e) {
            var cursor = e.target.result;
            if(cursor) {
                s += "<p><h2>Key "+cursor.key+"</h2></p>";
                for(var field in cursor.value) {
                    s+= field+"="+cursor.value[field]+"<br/>";
                }
                s+="</p>";
                cursor.continue();
             }
             document.querySelector("#status3").innerHTML = s;
         }
    }
    
    </script>
    
    <p>添加数据<br/>
    <input type="text" id="name" placeholder="Name"><br/>
    <input type="email" id="email" placeholder="Email"><br/>
    <button id="addButton">Add Data</button>
    </p>
    
    <p>根据Key查询数据<br/>
    <input type="text" id="key" placeholder="Key"><br/>
    <button id="getButton">Get Data</button>
    </p>
    <div id="status" name="status"></div>
    
    <p>获取所有数据<br/>
    <button id="getAllButton">Get EveryOne</button>
    </p>
    <div id="status2" name="status2"></div>
    
    <p>根据索引:Name查询数据<br/>
        <input type="text" id="name1" placeholder="Name"><br/>
        <button id="getByName">Get ByName</button>
    </p>
    <div id="status3" name="status3"></div>
    

    将方面的代码复制到 indexed_db.html 中,用 谷歌 Chrome
    浏览器打开,就可以加上、查询数据。在 Chrome
    的开发者工具中,能查看创制的 DB
    、存储对象(可驾驭成表)以及表中添加的多寡。

    NoSQL 13

    IndexedDB 有个至极有力的职能,就是 index(索引)。它可对 Value
    对象中其余属性生成索引,然后可以依照索引举行 Value 对象的高速查询。

    要生成索引或支撑索引查询数据,须要在首次生成存储对象时,调用接口生成属性的目录。可以同时对目的的多少个不等性质创建索引。如上边代码就对name
    和 email 七个特性都生成了目录。

    var objectStore = thisDB.createObjectStore("people",{ autoIncrement:true });
    //first arg is name of index, second is the path (col);
    objectStore.createIndex("name","name", {unique:false});
    objectStore.createIndex("email","email", {unique:true});
    

    生成索引后,就足以根据索引进行多少的查询。

    function getPeopleByNameIndex(e)
    {
    var name = document.querySelector("#name1").value;
    
    var transaction = db.transaction(["people"],"readonly");
    var store = transaction.objectStore("people");
    var index = store.index("name");
    
    //name is some value
    var request = index.get(name);
    request.onsuccess = function(e) {
        var result = e.target.result;
        if(result) {
            var s = "<p><h2>Name "+name+"</h2><p>";
            for(var field in result) {
                s+= field+"="+result[field]+"<br/>";
            }
            s+="</p>";
        } else {
            document.querySelector("#status3").innerHTML = "<h2>No match!</h2>";
        }
      }
    }
    

    分析:IndexedDB 是一种灵活且成效强大的数额存储机制,它集合了 Dom
    Storage 和 Web SQL Database
    的优点,用于存储大块或复杂结构的多寡,提供更大的蕴藏空间,使用起来也比较不难。可以看做
    Web SQL Database 的代表。不太相符静态文件的缓存。

  18. 以key-value 的形式存取对象,可以是任何类型值或对象,包含二进制。

  19. 可以对目的任何属性生成索引,方便查询。

  20. 较大的存储空间,默许推荐250MB(分 HOST),比 Dom Storage 的5MB
    要大的多。
  21. 透过数据库的事务(tranction)机制进行多少操作,有限支撑数据一致性。
  22. 异步的 API 调用,防止造成等待而影响体验。

    Android 在4.4从头参加对 IndexedDB 的辅助,只需打开允许 JS
    执行的开关就好了。

    WebView myWebView = (WebView) findViewById(R.id.webview);
    WebSettings webSettings = myWebView.getSettings();
    webSettings.setJavaScriptEnabled(true);
    

    #### 2.6 File System API

    File System API 是 H5 新加盟的囤积机制。它为 Web App
    提供了一个虚拟的文件系统,就如 Native App
    访问当地文件系统一样。由于安全性的考虑,那一个虚拟文件系统有肯定的范围。Web
    App
    在编造的文件系统中,可以开展文件(夹)的开创、读、写、删除、遍历等操作。

    File System API 也是一种可选的缓存机制,和前面的
    SQLDatabase、IndexedDB 和 AppCache 等一律。File System API
    有友好的部分一定的优势:

  23. 可以满意大块的二进制数据( large binary blobs)存储须求。

  24. 可以因而预加载资源文件来增长品质。

  25. 可以从来编辑文件。

    浏览器给虚拟文件系统提供了两种类型的囤积空间:临时的和持久性的。临时的仓储空间是由浏览器自动分配的,但恐怕被浏览器回收;持久性的蕴藏空间必要显示的报名,申请时浏览器会给用户一提醒,须要用户展开确认。持久性的积存空间是
    WebApp
    自己管理,浏览器不会回收,也不会去掉内容。持久性的仓储空间尺寸是通过配额来保管的,首次提请时会一个方始的配额,配额用完须求重新报名。

    编造的文件系统是运作在沙盒中。差距 WebApp
    的虚拟文件系统是互为隔离的,虚拟文件系统与地面文件系统也是并行隔离的。

    File System API
    提供了一组文件与公事夹的操作接口,有联手和异步四个本子,可满意区其余使用处境。下边通过一个文件成立、读、写的例证,演示下简单的成效与用法。

  26.  

    将地方代码复制到 file_system_api.html 文件中,用 谷歌(Google) Chrome
    浏览器打开(现在 File System API 唯有 Chrome 43+、Opera 32+ 以及
    Chrome for Android 46+ 那七个浏览器援助)。由于 谷歌 Chrome
    禁用了地方 HTML 文件中的 File System API效率,在起步 Chrome
    时,要添加”—allow-file-access-from-files“命令行参数。

    NoSQL 14

    上边截图,左侧是 HTML 运行的结果,左侧是 Chrome 开发者工具中来看的
    Web 的文件系统。基本上
    H5的两种缓存机制的数据都能在这么些开发者工具看到,格外有益。

    解析:File System API 给 Web App 带来了文件系统的效益,Native
    文件系统的效益在 Web App
    中都有对应的兑现。任何索要经过文件来保管数据,或通过文件系统进行多少管理的场合都相比较符合。

    到当下,Android 系统的 Webview 还不援救 File System API。


3 移动端 Web 加载品质(缓存)优化

分析完 H5提供的各样缓存机制,回到移动端(针对 Android,可能也适用于
iOS)的情景。现在 Android App(包蕴手 Q 和 WX)大多嵌入了 Webview
的机件(系统 Webview 或 QQ 游览器的 X5组件),通过内嵌Webview
来加载一些H5的营业移动页面或音讯页。那样可充足发挥Web前端的优势:飞快支付、发表,灵活上下线。但
Webview
也有一对不得忽略的难题,比较杰出的就是加载相对较慢,会相对消耗较多流量。

由此对一些 H5页面举行调节及抓包发现,每一次加载一个
H5页面,都会有较多的哀告。除了 HTML 主 URL 自身的伏乞外,HTML外部引用的
JS、CSS、字体文件、图片都是一个独门的 HTTP
请求,每一个伸手都串行的(可能有连接复用)。这么多请求串起来,再加上浏览器解析、渲染的时光,Web
全体的加载时间变得较长;请求文件更加多,消耗的流量也会越来越多。大家可概括应用方面说到二种缓存机制,来赞助大家优化
Web 的加载品质。

NoSQL 15

敲定:综合各样缓存机制相比较,对于静态文件,如
JS、CSS、字体、图片等,适合通过浏览器缓存机制来开展缓存,通过缓存文件可大幅升高Web
的加载速度,且节省流量。但也有一对相差:缓存文件须求首次加载后才会暴发;浏览器缓存的贮存空间有限,缓存有被扫除的或者;缓存的文本并未校验。要解决那么些不足,可以参照手
Q 的离线包,它实用的解决了那个不足。

对于 Web 在地点或服务器获取的多少,可以通过 Dom Storage 和 IndexedDB
举办缓存。也在自然水准上压缩和 Server
的相互,升高加载速度,同时节约流量。

理所当然 Web 的品质优化,还包含精选适用的图片大小,防止 JS 和 CSS
造成的堵塞等。那就需求 Web
前端的同事按照局地正式和局地调节工具举行优化了。

想了解更加多干货,请搜索关切群众号:腾讯Bulgy,或探寻微信号:weixinBugly,关心大家


腾讯Bugly

Bugly是腾讯里面产品质量监控平台的外发版本,支持iOS和Android两大主流平台,其根本作用是App发布之后,对用户侧暴发的crash以及卡顿现象开展督察并上报,让开发同学可以第一时间领悟到app的身分境况,及时修改。方今腾讯里面有着的产品,均在应用其进行线上产品的崩溃监控。

腾讯里面社团4年打磨,如今腾讯内部装有的出品都在应用,基本覆盖了炎黄市面的移动装备以及互连网环境,可信性有保管。使用Bugly,你就动用了和手机QQ、QQ空间、手机管家相同的成色保持手段

网站地图xml地图