欢迎来到山村网

HTML5移动应用开发第2章:移动web应用的本地存储

2019-03-30 09:44:12浏览:650 来源:山村网   
核心摘要:在本文中,您将使用最新 Web 技术开发 Web 应用程序。这里的大多数代码只是 HTML、JavaScript 和 CSS任何 Web 开发人员的核心技

在本文中,您将使用最新 Web 技术开发 Web 应用程序。这里的大多数代码只是 HTML、Javascript 和 CSS — 任何 Web 开发人员的核心技术。需要的最重要的东西是用于测试代码的浏览器。本文中的大多数代码将运行在最新的桌面浏览器上,例外的情况会指出来。当然,还必须在移动浏览器上进行测试,您肯定希望最新的 iPhone 和 Android SDK 支持这些代码。本文中使用的是 iPhone SDK 3.1.3 和 Android SDK 2.1。

本地存储基础

Web 开发人员多年来一直在尝试将数据存储在客户机上。HTTP cookies 被滥用于此目的。开发人员将大量数据挤放在 HTTP 规范分配的 4KB 上。原因很简单。出于各种原因,交互式 Web 应用程序需要存储数据,并且将这些数据存储在服务器上通常效率低下、不安全或者不适当。多年来,这个问题有了好几种备选方法。各种各样的浏览器已经引入了专有存储 API。开发人员也利用了 Flash Player 中的扩展存储功能(通过 Javascript 实现)。类似地,Google 为各种浏览器创建了 Gears 插件,并且它包含了存储 API。毫不奇怪的是,一些 Javascript 库试图抹平这些差异。换句话说,这些库提供一个简单的 API,然后检查有哪些存储功能(可能是一个专有浏览器 API 或者是一个诸如 Flash 的插件)。

对 Web 开发人员来说幸运的是,HTML 5 规范最终包含了一个针对本地存储的标准,被广泛的浏览器所实现。事实上,该标准是最快被采纳的标准,在所有主要浏览器的最新版本中都受到支持:Microsoft®、Internet Explorer®、Mozilla Firefox、Opera、Apple Safari 和 Google Chrome。对于移动开发人员更为重要的是,它在基于 WebKit 的浏览器(诸如 iPhone 和使用 Android(版本 2.0 或更高版本)的手机中的浏览器)以及其他移动浏览器(比如 Mozilla 的 Fennec)中受到支持。记住这一点,我们来看一下这个 API。

StorageAPI

localStorageAPI 十分简单。实际上,根据 HTML 5 规范,它实现了 DOM Storage 接口。差别的原因是,HTML 5 指定两个不同的对象实现该接口:localStorage和sessionStorage。sessionStorage对象是一个只在会话期间存储数据的Storage实现。更确切地说,只要没有可以访问sessionStorage的脚本正在运行,浏览器就可以删除sessionStorage数据。这是与localStorage相对的,后者跨多个用户会话。两个对象共享相同的 API,所以我将只着重介绍localStorage。

StorageAPI 是一种经典的名/值对数据结构。您将使用的最常见的方法是getItem(name)和setItem(name, value)。这些方法完全跟您预期的一样:getItem返回与名称相关联的值,如果什么都不存在,则返回 null,而setItem要么是将名/值对添加到localStorage,要么是取代现有值。还有一个removeItem(name),顾名思意,它从localStorage删除一个名/值对(如果存在的话,否则什么都不做)。最后,对于在所有名/值对上迭代,存在两个 API。一个是长度属性,给出正在存储的名/值对的总数。对应地,一个key(index)方法从存储中使用的所有名称中返回一个名称。

利用这些简单的 API,可以完成大量任务,比如说个性化或跟踪用户行为。这些可以说对移动 Web 开发人员是重要的用例,但是还有一个更为重要的用例:高速缓存。利用localStorage,可以在客户机的本地机器上容易地从服务器高速缓存数据。这让您无需等待可能缓慢的服务器回调,并且最小化了对服务器上数据的需求量。现在来看一个例子,演示了如何使用 localStorage 来获得这种高速缓存。

例子:利用本地存储实现高速缓存

本例建立在本系列第 1 部分中的例子之上,那时您最先开始了 t0 开发。那个例子展示了如何通过利用地理定位API 取得用户的位置而执行 Twitter 的本地搜索。从那个例子开始,对它进行简化,并大大提高它的性能。首先,将那个例子简化成不带地理位置的 Twitter 搜索。清单 1展示了简化的 Twitter 搜索应用程序。


清单 1. 最基本的 Twitter 搜索

XML/HTML Code复制内容到剪贴板
  1. <html>
  2. <head>
  3. <metahttp-equiv="Content-Type"content="text/html;charset=UTF-8">
  4. <metaname="viewport"content="width=device-width"/>
  5. <title>BasicTwitterSearch</title>
  6. <scripttype="text/javascript">
  7. functionsearchTwitter(){
  8. varquery="http://search.twitter.com/search.json?callback
  9. =showResults&q=";
  10. query+=$("kwBox").value;
  11. varscript=document.createElement("script");
  12. script.src=query;
  13. document.getElementsByTagName("head")[0].appendChild(script);
  14. }
  15. //uicodedeletedforbrevity
  16. functionshowResults(response){
  17. vartweets=response.results;
  18. tweets.forEach(function(tweet){
  19. tweet.linkUrl="http://twitter.com/"+tweet.from_user
  20. +"/status/"+tweet.id;
  21. });
  22. makeResultsTable(tweets);
  23. }
  24. </script>
  25. <!--CSSdeletedforbrevity-->
  26. </head>
  27. <body>
  28. <divid="main">
  29. <labelfor="kwBox">SearchTwitter:</label>
  30. <inputtype="text"id="kwBox"/>
  31. <inputtype="button"value="Go!"onclick="searchTwitter()"/>
  32. </div>
  33. <divid="results">
  34. </div>
  35. </body>
  36. </html>

在这个应用程序中,使用了 Twitter 搜索 API 对 JSONP 的支持。用户提交搜索时,会动态添加一个脚本标记到页面并指定回调函数的名称,从而进行一次 API 调用。这允许您从 Web 页面进行一次跨域调用。一旦调用返回,回调函数(showResults)就会被调用。您添加一个链接 URL 到 Twitter 返回的每个 tweet,然后创建一个简单的表格用于显示这些 tweet。为了提速,您可以高速缓存从搜索查询得到的结果,然后在用户每次提交查询时使用这些缓存的结果。首先来看如何使用localStorage来本地存储 tweet。

本地保存

基本的 Twitter 搜索将从 Twitter 搜索 API 提供一组 tweet。如果您可以本地保存这些 tweet,并将它们与生成它们的关键词搜索相关联,那么您就具有了一个有用的高速缓存。要保存 tweet,您只需要修改当对 Twitter 搜索 API 的调用返回时将被调用的callback函数。清单 2展示了修改后的函数。


清单 2. 搜索和保存

Javascript Code复制内容到剪贴板
  1. functionsearchTwitter(){
  2. varkeyword=$("kwBox").value;
  3. varquery="http://search.twitter.com/search.json?callback
  4. =processResults&q=";
  5. query+=keyword;
  6. varscript=document.createElement("script");
  7. script.src=query;
  8. document.getElementsByTagName("head")[0].appendChild(script);
  9. }
  10. functionprocessResults(response){
  11. varkeyword=$("kwBox").value;
  12. vartweets=response.results;
  13. tweets.forEach(function(tweet){
  14. saveTweet(keyword,tweet);
  15. tweet.linkUrl="http://twitter.com/"+tweet.from_user+"/status/"+tweet.id;
  16. });
  17. makeResultsTable();
  18. addTweetsToResultsTable(tweets);
  19. }
  20. functionsaveTweet(keyword,tweet){
  21. //checkifthebrowsersupportslocalStorage
  22. if(!window.localStorage){
  23. return;
  24. }
  25. if(!localStorage.getItem("tweet"+tweet.id)){
  26. localStorage.setItem("tweet"+tweet.id,JSON.stringify(tweet));
  27. }
  28. varindex=localStorage.getItem("index::"+keyword);
  29. if(index){
  30. index=JSON.parse(index);
  31. }else{
  32. index=[];
  33. }
  34. if(!index.contains(tweet.id)){
  35. index.push(tweet.id);
  36. localStorage.setItem("index::"+keyword,JSON.stringify(index));
  37. }
  38. }

从第一个函数searchTwitter开始。这在用户提交搜索时被调用。相对于清单 1做了改动的惟一的地方是callback函数。不只是在 tweet 返回时显示它们,您还需要处理它们(除了显示,还要保存它们)。因此,您指定一个新的callback函数processResults。您针对每个 tweet 调用saveTweet。您还传递被用于生成搜索结果的关键词。这是因为您想要将这些 tweet 与该关键词相关联。

在saveTweet函数中,首先进行检查,确保localStorage真正受到浏览器的支持。正如前面所提到的,localStorage 在桌面和移动浏览器中都受到广泛支持,但是在使用这样的新特性时进行检查总是一个好主意。如果它不受支持,那么您简单地从函数返回。显然不会保存任何东西,但是也不会报错 — 应用程序在这种情况下只是不会具有高速缓存。如果localStorage受到支持,那么首先进行检查,看这个 tweet 是否已经存储。如果没有存储,那么使用setItem本地存储它。接下来,检索一个对应于关键词的索引对象。这只是一组与关键词相关联的 tweet 的 ID。如果 tweet ID 还不是索引的一部分,那么添加它并更新索引。

注意,在清单 3中保存和加载 JSON 时,您使用了JSON.stringify和JSON.parse。JSON 对象(或者更确切地说,是window.JSON)是 HTML 5 规范的一部分,作为一个总是存在的原生对象。stringify方法将把任何 Javascript 对象转换成一个序列化的字符串,而parse方法则进行相反的操作,它从序列化的字符串表示还原 Javascript 对象。这是很必要的,因为localStorage只存储字符串。但是,原生 JSON 对象并不被广泛实现为localStorage。例如,它不出现在 iPhone(在撰写本文时是版本 3.1.3)的最新 Mobile Safari 浏览器上。它在最新 Android 浏览器上受支持。您可以容易地检查它是否在那里,如果不在,就加载一个额外的 Javascript 文件。您可以通过访问 json.org Web 站点(参见参考资料),获得原生使用的相同 JSON 对象。要本地查看这些序列化的字符串是什么样的,可以使用各种浏览器工具检查 localStorage 中为给定站点存储的内容。图 1展示了一些高速缓存的 tweet,它们存储在本地,使用 Chrome 的 Developer Tools 进行查看。


图 1. 本地高速缓存的 tweet
一系列本地高速缓存的 tweet 的屏幕截图(带有 Key 和 Value 字段)

Chrome 和 Safari 都内置了开发人员工具,可以用于查看任何保存在localStorage中的数据。这对于调试使用localStorage的应用程序非常有用。它以纯文本形式展示本地存储的键/值对。既然您已经开始保存来自 Twitter 的搜索 API 的 tweet,以便它们可以被用作高速缓存,所以您只需开始从localStorage读取它们即可。下面来看这是如何做到的。

快速本地数据加载

在清单 2中,您看到了一些例子使用getItem方法从localStorage读取数据。现在当一个用户提交搜索时,您可以检查高速缓存命中情况,并立即加载缓存的结果。当然,您仍将针对 Twitter 搜索 API 进行查询,因为人们一直在产生 tweet 并添加到搜索结果。但是,通过只寻找还没在高速缓存中的结果,现在您也有了让查询更为高效的方式。清单 3展示了更新后的搜索代码。


清单 3. 首先进行本地搜索

Javascript Code复制内容到剪贴板
  1. functionsearchTwitter(){
  2. if($("resultsTable")){
  3. $("resultsTable").innerHTML="";//clearresults
  4. }
  5. makeResultsTable();
  6. varkeyword=$("kwBox").value;
  7. varmaxId=loadLocal(keyword);
  8. varquery="http://search.twitter.com/search.json?callback=processResults&q=";
  9. query+=keyword;
  10. if(maxId){
  11. query+="&since_id="+maxId;
  12. }
  13. varscript=document.createElement("script");
  14. script.src=query;
  15. document.getElementsByTagName("head")[0].appendChild(script);
  16. }
  17. functionloadLocal(keyword){
  18. if(!window.localStorage){
  19. return;
  20. }
  21. varindex=localStorage.getItem("index::"+keyword);
  22. vartweets=[];
  23. vari=0;
  24. vartweet={};
  25. if(index){
  26. index=JSON.parse(index);
  27. for(i=0;i<index.length;i++){
  28. tweet=localStorage.getItem("tweet"+index[i]);
  29. if(tweet){
  30. tweet=JSON.parse(tweet);
  31. tweets.push(tweet);
  32. }
  33. }
  34. }
  35. if(tweets.length<1){
  36. return0;
  37. }
  38. tweets.sort(function(a,b){
  39. returna.id>b.id;
  40. });
  41. addTweetsToResultsTable(tweets);
  42. returntweets[0].id;
  43. }

您将注意到的第一件事情是,当一个搜索被提交时,您首先调用新的loadLocal函数。该函数返回一个整数,即高速缓存中找到的最新 tweet 的 ID。loadLocal函数接受一个keyword作为参数,该关键词也被用于在localStorage高速缓存中寻找相关 tweet。如果具有一个maxId,那么使用它来修改对 Twitter 的查询,添加since_id参数。您在告诉 Twitter API 只返回比该参数中给定的 ID 新的 tweet。潜在地,这可以减少从 Twitter 返回的结果数量。您任何时候都可以为移动 Web 应用程序优化服务器调用,因为它可以真正改善慢速移动网络上的用户体验。现在更仔细地来看一下loadLocal。

在loadLocal函数中,您利用了存储在前面清单 2中的数据结构。通过使用getItem,您首先加载与关键词相关联的索引。如果没找到任何索引,那么就没有缓存的 tweet,所以就没有展示的东西,并且没有可对查询进行的优化(您返回一个 0 值以指示这一点)。如果找到一个索引,那么您从它得到 ID 列表。这些 tweet 中的每一个都被本地高速缓存,所以您只需再次使用getItem方法,从高速缓存加载每一个 tweet。加载的 tweet 然后被排序。使用addTweetsToResultsTable函数来显示 tweet,然后返回最新 tweet 的 ID。在本例中,得到新 tweet 的 代码直接调用更新 UI 的函数。您可能会对此感到惊讶,因为它在存储和检索 tweet 的代码与显示它们的代码之间创建了耦合,全都通过processResults函数。使用存储事件会提供一种备选的、更少耦合的方法。

存储事件

现在扩展示例应用程序,展示最可能具有缓存结果的前 10 个搜索条目。这可能代表用户最常提交的搜索。清单 4展示了一个用于计算并显示前 10 个搜索条目的函数。


清单 4. 计算前 10 个搜索条目

Javascript Code复制内容到剪贴板
  1. functiondisplayStats(){
  2. if(!window.localStorage){return;}
  3. vari=0;
  4. varkey="";
  5. varindex=[];
  6. varcachedSearches=[];
  7. for(i=0;i<localStorage.length;i++){
  8. key=localStorage.key(i);
  9. if(key.indexOf("index::")==0){
  10. index=JSON.parse(localStorage.getItem(key));
  11. cachedSearches.push({keyword:key.slice(7),numResults:index.length});
  12. }
  13. }
  14. cachedSearches.sort(function(a,b){
  15. if(a.numResults==b.numResults){
  16. if(a.keyword.toLowerCase()<b.keyword.toLowerCase()){
  17. return-1;
  18. }elseif(a.keyword.toLowerCase()>b.keyword.toLowerCase()){
  19. return1;
  20. }
  21. return0;
  22. }
  23. returnb.numResults-a.numResults;
  24. }).slice(0,10).forEach(function(search){
  25. varli=document.createElement("li");
  26. vartxt=document.createTextNode(search.keyword+":"+search.numResults);
  27. li.appendChild(txt);
  28. $("stats").appendChild(li);
  29. });
  30. }

该函数充分展示了localStorageAPI。您首先得到存储在localStorage中的条目的总数,然后再迭代这些条目。如果条目是索引,那么您就解析该对象并创建一个表示您要处理的数据的对象:与索引相关联的关键词和索引中 tweet 的数量。该数据存储在一个叫做cachedSearches的数组中。接下来,排序cachedSearches,将具有最多结果的搜索排在第一位,如果两个搜索具有相同数量的缓存结果,就再使用一个不区分大小写的字母排序。然后对于前 10 个搜索,为每个搜索创建 HTML,并将它们附加到一个排好序的列表。让我们在页面初次加载时调用该函数,如清单 5所示。


清单 5. 初始化页面

window.onload = function() {    displayStats();    document.body.setAttribute("onstorage", "handleonStorage();");}

第一行在页面加载时调用清单 4中的函数。第二次加载是变得更有趣的地方。您在这里为onstorage事件设置一个事件处理程序。每当localStorage.setItem函数执行完成,该事件就会激活。这将允许您重新计算前 10 个搜索。清单 6展示了该事件处理程序。


清单 6. Storage 事件处理程序

function handleonStorage() {    if (window.event && window.event.key.indexOf("index::") == 0){        $("stats").innerHTML = "";        displayStats();    }}

onstorage事件将与窗口相关联。它具有几个有用的属性:key、oldValue和newValue。除了这些自解释的属性之外,它还有一个url(更改值的页面的 URL)和source(包含更改值的脚本的窗口)。如果用户具有多个到应用程序的窗口或选项卡或者甚至是 iframes,那么这最后两个属性就更有用,但是没有哪一个在移动应用程序中特别常见。回到清单 6,您真正需要的惟一的属性是key属性。您使用该属性来看它是不是一个已修改的索引。如果是的,那么您重新设置前 10 名列表,并通过再次调用displayStats函数而重新绘制它。该技术的优点是,其他函数都不需要了解前 10 名列表,因为它是自包含的。

前面我提到过,DOM Storage(它包含localStorage和sessionStorage)总体来说是一个被广泛采纳的 HTML 5 特性。但是,存储事件对于这一点来说是一个例外 — 至少在桌面浏览器上如此。在撰写本文时,仅有的支持存储事件的桌面浏览器是 Safari 4+ 和 Internet Explorer 8+。在 Firefox、Chrome 和 Opera 中不受支持。但是在移动领域,情况稍有好转。iPhone 和 Android 浏览器的最新版都完全支持存储事件,并且这里给出的代码都能在这些浏览器中完美地运行。

结束语

作为一名开发人员,突然在客户机上拥有巨额的存储空间,您会觉得自己获得了很大的解放。对于长期的 Web 开发人员来说,为做到他们多年来一直想做、却苦于找不到好的方式来做的事情带来了转机。对于移动开发人员来说,则更为振奋人心,因为它真正开启了数据的本地高速缓存。除了大大改善应用程序的性能之外,本地高速缓存对于推进移动 Web 应用程序的另一个新的令人振奋的功能 —— 离线 —— 是很关键的。这将是本系列下一篇文章的主题。

(责任编辑:豆豆)
下一篇:

HTML5移动应用开发第3章:移动Web离线应用

上一篇:

HTML5移动应用开发第1章:用HTML5、地理定位API和Web服务来开发移动应用

  • 信息二维码

    手机看新闻

  • 分享到
打赏
免责声明
• 
本文仅代表作者个人观点,本站未对其内容进行核实,请读者仅做参考,如若文中涉及有违公德、触犯法律的内容,一经发现,立即删除,作者需自行承担相应责任。涉及到版权或其他问题,请及时联系我们 xfptx@outlook.com