热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Web应用中的离线数据存储

为了提升Web应用的用户体验,想必很多开发者都会项目中引入离线数据存储机制。可是面对各种各样的离线数据技术,哪一种才是最能满足项目需求的呢?本文将帮助各位找到最合适的那一个。引言随

为了提升Web应用的用户体验,想必很多开发者都会项目中引入离线数据存储机制。可是面对各种各样的离线数据技术,哪一种才是最能满足项目需求的呢?本文将帮助各位找到最合适的那一个。

引言

随着HTML5的到来,各种Web离线数据技术进入了开发人员的视野。诸如AppCache、localStorage、sessionStorage和IndexedDB等等,每一种技术都有它们各自适用的范畴。比如AppCache就比较适合用于离线起动应用,或者在离线状态下使应用的一部分功能照常运行。接下来我将会为大家作详细介绍,并且用一些代码片段来展示如何使用这些技术。

AppCache

如果你的Web应用中有一部分功能(或者整个应用)需要在脱离服务器的情况下使用,那么就可以通过AppCache来让你的用户在离线状态下也能使用。你所需要做的就是创建一个配置文件,在其中指定哪些资源需要被缓存,哪些不需要。此外,还能在其中指定某些联机资源在脱机条件下的替代资源。

AppCache的配置文件通常是一个以.appcache结尾的文本文件(推荐写法)。文件以CACHE MANIFEST开头,包含下列三部分内容:

  • CACHE – 指定了哪些资源在用户第一次访问站点的时候需要被下载并缓存
  • NETWORK – 指定了哪些资源需要在联机条件下才能访问,这些资源从不被缓存
  • FALLBACK – 指定了上述资源在脱机条件下的替代资源

示例

首先,你需要在页面上指定AppCache的配置文件:



...

在这里千万记得在服务器端发布上述配置文件的时候,需要将MIME类型设置为text/cache-manifest,否则浏览器无法正常解析。

接下来是创建之前定义好的各种资源。我们假定在这个示例中,你开发的是一个交互类站点,用户可以在上面联系别人并且发表评论。用户在离线的状态下依然可以访问网站的静态部分,而联系以及发表评论的页面则会被其它页面替代,无法访问。

好的,我们这就着手定义那些静态资源:

CACHE MANIFEST
CACHE:
/about.html
/portfolio.html
/portfolio_gallery/image_1.jpg
/portfolio_gallery/image_2.jpg
/info.html
/style.css
/main.js
/jquery.min.js

备注:配置文件写起来有一点很不方便。举例来说,如果你想缓存整个目录,你不能直接在CACHE部分使用通配符(*),而是只能在NETWORK部分使用通配符把所有不应该被缓存的资源写出来。

你不需要显式地缓存包含配置文件的页面,因为这个页面会自动被缓存。接下来我们为联系和评论的页面定义FALLBACK部分:

FALLBACK:
/contact.html /offline.html
/comments.html /offline.html

最后我们用一个通配符来阻止其余的资源被缓存:

NETWORK:
*

最后的结果就是下面这样:

CACHE MANIFEST
CACHE:
/about.html
/portfolio.html
/portfolio_gallery/image_1.jpg
/portfolio_gallery/image_2.jpg
/info.html
/style.css
/main.js
/jquery.min.js
FALLBACK:
/contact.html /offline.html
/comments.html /offline.html
NETWORK:
*

还有一件很重要的事情要记得:你的资源只会被缓存一次!也就是说,如果资源更新了,它们不会自动更新,除非你修改了配置文件。所以有一个最佳实践是,在配置文件中增加一项版本号,每次更新资源的时候顺带更新版本号:

CACHE MANIFEST
# version 1
CACHE:
...

LocalStorage 和 SessionStorage

如果你想在Javascript代码里面保存些数据,那么这两个东西就派上用场了。前一个可以保存数据,永远不会过期(expire)。只要是相同的域和端口,所有的页面中都能访问到通过LocalStorage保存的数据。举个简单的例子,你可以用它来保存用户设置,用户可以把他的个人喜好保存在当前使用的电脑上,以后打开应用的时候能够直接加载。后者也能保存数据,但是一旦关闭浏览器窗口(译者注:浏览器窗口,window,如果是多tab浏览器,则此处指代tab)就失效了。而且这些数据不能在不同的浏览器窗口之间共享,即使是在不同的窗口中访问同一个Web应用的其它页面。

备注:有一点需要提醒的是,LocalStorage和SessionStorage里面只能保存基本类型的数据,也就是字符串和数字类型。其它所有的数据可以通过各自的toString()方法转化后保存。如果你想保存一个对象,则需要使用JSON.stringfy方法。(如果这个对象是一个类,你可以复写它默认的toString()方法,这个方法会自动被调用)。

示例

我们不妨来看看之前的例子。在联系人和评论的部分,我们可以随时保存用户输入的东西。这样一来,即使用户不小心关闭了浏览器,之前输入的东西也不会丢失。对于jQuery来说,这个功能是小菜一碟。(注意:表单中每个输入字段都有id,在这里我们就用id来指代具体的字段)

$('#comments-input, .contact-field').on('keyup', function () {
// let's check if localStorage is supported
if (window.localStorage) {
localStorage.setItem($(this).attr('id'), $(this).val());
}
});

每次提交联系人和评论的表单,我们需要清空缓存的值,我们可以这样处理提交(submit)事件:

$('#comments-form, #contact-form').on('submit', function () {
// get all of the fields we saved
$('#comments-input, .contact-field').each(function () {
// get field's id and remove it from local storage
localStorage.removeItem($(this).attr('id'));
});
});

最后,每次加载页面的时候,把缓存的值填充到表单上即可:

// get all of the fields we saved
$('#comments-input, .contact-field').each(function () {
// get field's id and get it's value from local storage
var val = localStorage.getItem($(this).attr('id'));
// if the value exists, set it
if (val) {
$(this).val(val);
}
});

IndexedDB

在我个人看来,这是最有意思的一种技术。它可以保存大量经过索引(indexed)的数据在浏览器端。这样一来,就能在客户端保存复杂对象,大文档等等数据。而且用户可以在离线情况下访问它们。这一特性几乎适用于所有类型的Web应用:如果你写的是邮件客户端,你可以缓存用户的邮件,以供稍后再看;如果你写的是相册类应用,你可以离线保存用户的照片;如果你写的是GPS导航,你可以缓存用户的路线……不胜枚举。

IndexedDB是一个面向对象的数据库。这就意味着在IndexedDB中既不存在表的概念,也没有SQL,数据是以键值对的形式保存的。其中的键既可以是字符串和数字等基础类型,也可以是日期和数组等复杂类型。这个数据库本身构建于存储(store,一个store类似于关系型数据中表的概念)的基础上。数据库中每个值都必须要有对应的键。每个键既可以自动生成,也可以在插入值的时候指定,也可以取自于值中的某个字段。如果你决定使用值中的字段,那么只能向其中添加Javascript对象,因为基础数据类型不像Javascript对象那样有自定义属性。

示例

在这个例子中,我们用一个音乐专辑应用作为示范。不过我并不打算在这里从头到尾展示整个应用,而是把涉及IndexedDB的部分挑出来解释。如果大家对这个Web应用感兴趣的话,文章的后面也提供了源代码的下载。首先,让我们来打开数据库并创建store:

// check if the indexedDB is supported
if (!window.indexedDB) {
throw 'IndexedDB is not supported!'; // of course replace that with some user-friendly notification
}
// variable which will hold the database connection
var db;
// open the database
// first argument is database's name, second is it's version (I will talk about versions in a while)
var request = indexedDB.open('album', 1);
request.Onerror= function (e) {
console.log(e);
};
// this will fire when the version of the database changes
request.Onupgradeneeded= function (e) {
// e.target.result holds the connection to database
db = e.target.result;
// create a store to hold the data
// first argument is the store's name, second is for options
// here we specify the field that will serve as the key and also enable the automatic generation of keys with autoIncrement
var objectStore = db.createObjectStore('cds', { keyPath: 'id', autoIncrement: true });
// create an index to search cds by title
// first argument is the index's name, second is the field in the value
// in the last argument we specify other options, here we only state that the index is unique, because there can be only one album with specific title
objectStore.createIndex('title', 'title', { unique: true });
// create an index to search cds by band
// this one is not unique, since one band can have several albums
objectStore.createIndex('band', 'band', { unique: false });
};

相信上面的代码还是相当通俗易懂的。估计你也注意到上述代码中打开数据库时会传入一个版本号,还用到了onupgradeneeded事件。当你以较新的版本打开数据库时就会触发这个事件。如果相应版本的数据库尚不存在,则会触发事件,随后我们就会创建所需的store。接下来我们还创建了两个索引,一个用于标题搜索,一个用于乐队搜索。现在让我们再来看看如何增加和删除专辑:

// adding
$('#add-album').on('click', function () {
// create the transaction
// first argument is a list of stores that will be used, second specifies the flag
// since we want to add something we need write access, so we use readwrite flag
var transaction = db.transaction([ 'cds' ], 'readwrite');
transaction.Onerror= function (e) {
console.log(e);
};
var value = { ... }; // read from DOM
// add the album to the store
var request = transaction.objectStore('cds').add(value);
request.Onsuccess= function (e) {
// add the album to the UI, e.target.result is a key of the item that was added
};
});
// removing
$('.remove-album').on('click', function () {
var transaction = db.transaction([ 'cds' ], 'readwrite');
var request = transaction.objectStore('cds').delete(/* some id got from DOM, converted to integer */);
request.Onsuccess= function () {
// remove the album from UI
}
});

是不是看起来直接明了?这里对数据库所有的操作都基于事务的,只有这样才能保证数据的一致性。现在最后要做的就是展示音乐专辑:

request.Onsuccess= function (e) {
if (!db) db = e.target.result;
var transaction = db.transaction([ 'cds' ]); // no flag since we are only reading
var store = transaction.objectStore('cds');
// open a cursor, which will get all the items from database
store.openCursor().Onsuccess= function (e) {
var cursor = e.target.result;
if (cursor) {
var value = cursor.value;
$('#albums-list tbody').append('
'+ value.title +''+ value.band +''+ value.genre +''+ value.year +'
‘); // move to the next item in the cursor cursor.continue(); } }; }

这也不是十分复杂。可以看见,通过使用IndexedDB,可以很轻松的保存复杂对象,也可以通过索引来检索想要的内容:

function getAlbumByBand(band) {
var transaction = db.transaction([ 'cds' ]);
var store = transaction.objectStore('cds');
var index = store.index('band');
// open a cursor to get only albums with specified band
// notice the argument passed to openCursor()
index.openCursor(IDBKeyRange.only(band)).Onsuccess= function (e) {
var cursor = e.target.result;
if (cursor) {
// render the album
// move to the next item in the cursor
cursor.continue();
}
});
}

使用索引的时候和使用store一样,也能通过游标(cursor)来遍历。由于同一个索引值名下可能有好几条数据(如果索引不是unique的话),所以这里我们需要用到IDBKeyRange。它能根据指定的函数对结果集进行过滤。这里,我们只想根据指定的乐队进行检索,所以我们用到了only()函数。也能使用其它类似于lowerBound(),upperBound()和bound()等函数,它们的功能也是不言自明的。

总结

可以看见,在Web应用中使用离线数据并不是十分复杂。希望通过阅读这篇文章,各位能够在Web应用中加入离线数据的功能,使得你们的应用更加友好易用。你可以在这里下载所有的源码,尝试一下,或者修修改改,或者用在你们的应用中。

原文:Real-World Off-Line Data Storage
转载自:伯乐在线 – njuyz


推荐阅读
  • 在探讨Hibernate框架的高级特性时,缓存机制和懒加载策略是提升数据操作效率的关键要素。缓存策略能够显著减少数据库访问次数,从而提高应用性能,特别是在处理频繁访问的数据时。Hibernate提供了多层次的缓存支持,包括一级缓存和二级缓存,以满足不同场景下的需求。懒加载策略则通过按需加载关联对象,进一步优化了资源利用和响应时间。本文将深入分析这些机制的实现原理及其最佳实践。 ... [详细]
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 本文详细介绍了使用 Python 进行 MySQL 和 Redis 数据库操作的实战技巧。首先,针对 MySQL 数据库,通过 `pymysql` 模块展示了如何连接和操作数据库,包括建立连接、执行查询和更新等常见操作。接着,文章深入探讨了 Redis 的基本命令和高级功能,如键值存储、列表操作和事务处理。此外,还提供了多个实际案例,帮助读者更好地理解和应用这些技术。 ... [详细]
  • Node.js 配置文件管理方法详解与最佳实践
    本文详细介绍了 Node.js 中配置文件管理的方法与最佳实践,涵盖常见的配置文件格式及其优缺点,并提供了多种实用技巧和示例代码,帮助开发者高效地管理和维护项目配置,具有较高的参考价值。 ... [详细]
  • 本文详细介绍了在MySQL中如何高效利用EXPLAIN命令进行查询优化。通过实例解析和步骤说明,文章旨在帮助读者深入理解EXPLAIN命令的工作原理及其在性能调优中的应用,内容通俗易懂且结构清晰,适合各水平的数据库管理员和技术人员参考学习。 ... [详细]
  • 本文深入解析了JDK 8中HashMap的源代码,重点探讨了put方法的工作机制及其内部参数的设定原理。HashMap允许键和值为null,但键为null的情况只能出现一次,因为null键在内部通过索引0进行存储。文章详细分析了capacity(容量)、size(大小)、loadFactor(加载因子)以及红黑树转换阈值的设定原则,帮助读者更好地理解HashMap的高效实现和性能优化策略。 ... [详细]
  • 在最近的项目中,我们广泛使用了Qt框架的网络库,过程中遇到了一些挑战和问题。本文旨在记录这些经验和解决方案,以便日后参考。鉴于我们的客户端GUI完全基于Qt开发,我们期望利用其强大的网络功能进行Fiddler网络数据包的捕获与分析,以提升开发效率和应用性能。 ... [详细]
  • 为了确保iOS应用能够安全地访问网站数据,本文介绍了如何在Nginx服务器上轻松配置CertBot以实现SSL证书的自动化管理。通过这一过程,可以确保应用始终使用HTTPS协议,从而提升数据传输的安全性和可靠性。文章详细阐述了配置步骤和常见问题的解决方法,帮助读者快速上手并成功部署SSL证书。 ... [详细]
  • Python 伦理黑客技术:深入探讨后门攻击(第三部分)
    在《Python 伦理黑客技术:深入探讨后门攻击(第三部分)》中,作者详细分析了后门攻击中的Socket问题。由于TCP协议基于流,难以确定消息批次的结束点,这给后门攻击的实现带来了挑战。为了解决这一问题,文章提出了一系列有效的技术方案,包括使用特定的分隔符和长度前缀,以确保数据包的准确传输和解析。这些方法不仅提高了攻击的隐蔽性和可靠性,还为安全研究人员提供了宝贵的参考。 ... [详细]
  • POJ 2482 星空中的星星:利用线段树与扫描线算法解决
    在《POJ 2482 星空中的星星》问题中,通过运用线段树和扫描线算法,可以高效地解决星星在窗口内的计数问题。该方法不仅能够快速处理大规模数据,还能确保时间复杂度的最优性,适用于各种复杂的星空模拟场景。 ... [详细]
  • 为了优化用户体验,本文探讨了如何调整下拉菜单的宽度。通过合理设置宽度,可以提升界面的美观性和易用性。文章提供了具体的代码示例,帮助开发者实现这一目标。例如,可以通过 CSS 或 JavaScript 来动态调整下拉菜单的宽度,确保其在不同设备和屏幕尺寸上都能保持良好的显示效果。 ... [详细]
  • 初探性能优化:入门指南与实践技巧
    在编程领域,常有“尚未精通编码便急于优化”的声音。为了从性能优化的角度提升代码质量,本文将带领读者初步探索性能优化的基本概念与实践技巧。即使程序看似运行良好,数据处理效率仍有待提高,通过系统学习性能优化,能够帮助开发者编写更加高效、稳定的代码。文章不仅介绍了性能优化的基础知识,还提供了实用的调优方法和工具,帮助读者在实际项目中应用这些技术。 ... [详细]
  • 基于Java和SSM框架的志愿者管理平台源代码分析与实现
    本研究针对基于Java和SSM框架的志愿者管理平台进行了详细的源代码分析与实现。该平台属于Java Web项目,采用Java EE技术栈,并结合了Spring、Spring MVC和MyBatis三大核心框架(非开源)。项目名称为“基于SSM的志愿者管理系统”,旨在提升志愿者管理的效率和规范性。通过对系统架构、模块设计及关键代码的深入解析,本文为开发者提供了全面的技术参考和实践指导。 ... [详细]
  • 在HTML5应用中,Accordion(手风琴,又称抽屉)效果因其独特的展开和折叠样式而广泛使用。本文探讨了三种不同的Accordion交互效果,通过层次结构优化信息展示和页面布局,提升用户体验。这些效果不仅增强了视觉效果,还提高了内容的可访问性和互动性。 ... [详细]
  • 本文详细介绍了 jQuery 的入门知识与实战应用,首先讲解了如何引入 jQuery 库及入口函数的使用方法,为初学者提供了清晰的操作指南。此外,还深入探讨了 jQuery 在实际项目中的多种应用场景,包括 DOM 操作、事件处理和 AJAX 请求等,帮助读者全面掌握 jQuery 的核心功能与技巧。 ... [详细]
author-avatar
手机用户2502853267
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有