H5存储(web Storage和web SQL)
一、Web Storage教程
1、概述:
对于Web Storage来说,实际上是Cookies存储的进化版。如果了解Cookie的人几乎一看Web Storage就会用,如果你从来没用过没了解过Cookie,没关系,看了这篇文章照样轻松玩转Web Storage。首先,学习Web Storage只需背熟这句口诀:“两个接口,四个函数”。2、口诀:
(1)两个接口:分别是localStorage和sessonStorage
(2)四个函数:分别是setItem、getItem、removeItem和clear3、localStorage:
(1)特性:
域内安全、永久保存。即客户端或浏览器中来自同一域名的所有页面都可访问localStorage数据且数据除了删除否则永久保存,但客户端或浏览器之间的数据相互独立。
(2)四个函数:
A. localStorage.setItem 存储数据信息到本地
B. localStorage.getItem 读取本地存储的信息
C. localStorage.removeItem 删除本地存储的信息
D. localStorage.clear 清空所以存储的信息
4、sessonStorage:
(1)特性:
会话控制、短期保存。会话概念与服务器端的session概念相似,短期保存指窗口或浏览器或客户端关闭后自动消除数据。
(2)四个函数:
A. sessionStorage.setItem 存储数据信息到本地
B. sessionStorage.getItem 读取本地存储的信息
C. sessionStorage.removeItem 删除本地存储的信息
D. sessionStorage.clear 清空所以存储的信息
5、四个函数的用法:
(1)localStorage.setItem(键名,键值)
在本地客户端存储一个字符串类型的数据,其中,第一个参数“键名”代表了该数据的标识符,而第二个参数“键值”为该数据本身。如:
localStorage.setItem("coffeeType", "mocha"); //存储键名为coffeeType和键值为mocha的数据到本地localStorage.setItem("coffeePrice", "28"); //有了上一句做参考,这句意思你该理解了吧
(2)localStorage.getItem(键名)
读取已存储在本地的数据,通过键名作为参数读取出对应键名的数据。如:
var data = localStorage.getItem("coffeeType"); //读取对应键名为coffeeType的数据
(3)localStorage.removeItem(键名)
移除已存储在本地的数据,通过键名作为参数删除对应键名的数据。如:
localStorage.removeItem("coffeeType"); //从本地存储中移除键名为coffeeType的数据(4)localStorage.clear()
移除本地存储所有数据。如:
localStorage.clear(); //保存着的"coffeePrice/28"键/值对也被移除了,所有本地数据拜拜
(5)另外,sessionStorage中的四个函数与以上localStorage类的函数用法基本一致,就不再详解。
6、兼容问题:
有人会说本地存储是H5的新贵,但是对于老、旧的浏览器来说怎么办?那就不用老古董浏览器呗,或者使用cookie作为替代。因为大多使用localStorage是用来存储字符串的,在其他编译型的语言看来,存储字符串能做些什么,但在javascript身上,旧大放光彩,可以存储JSON格式的字符串来扩展应用,可以存储类名变量值等等信息再通过eval()来感受使用JS的快感。既然localStorage是存储字符串的,那么在老古董浏览器上,可以通过使用Cookies来做替代方案并做好域内安全。
二、Web Storage应用
1、基本使用:
我们先把上面的四个函数整理一下,并加入一段验证代码用于检测本地的数据存储的存在情况。
( localStorage 使用测试 )
- <script type="text/javascript">
- localStorage.setItem("coffeeType", "mocha");
- localStorage.setItem("coffeePrice", "28");
- verify(); //验证本地存储
- localStorage.removeItem("coffeeType");
- verify(); //验证coffeeType是否存在
- localStorage.clear();
- verify(); //验证coffeeType和coffeePrice是否存在
- //自定义验证函数,验证coffeeType和coffeePrice的数据是否存在
- function verify(){
- var type = localStorage.getItem("coffeeType");
- var price = localStorage.getItem("coffeePrice");
- type = type ? type : '不存在';
- price = price ? price : '不存在';
- alert( "coffeeType: " + type + "\n\n" + "coffeePrice: " + price );
- }
- </script>
如果上面代码执行后弹出框提示不存在的话,那么意味着本地不支持H5的 localStorage 本地存储。那么不支持怎么办,DON'T 担心,可以写一段代码来兼容使用,请继续看下去。
2、兼容使用:
在写兼容代码前,再来说一点关于Web Storage的内容,在Web Storage的两个类中,我们比较常用的是localStorage类,至于session的话就交给后台去写吧。但是localStorage类在不支持H5的时候使用不了,所以我们将localStorage的四个函数封装一下,使得当浏览器或客户端不兼容localStorage时自动切换到Cookies存储。首先,要写好一个操作cookie的类和函数,将四个函数的名字和参数还有功能和localStorage保持一致。正好,《JavaScript权威指南》第五版里已经写好了一个cookieStorage代码,但是呢,这段代码有BUG的,和稍微与localStorage不一致,所以我修改了一下,花了点时间造下轮子,代码如下:
( cookieStorage.js )
- //《JavaScript权威指南》一书中有实现基于cookie的存储API,我把代码敲下来
- // 另外,书中的代码有错,以下为无BUG版并修改成1000天相当长的存储时间
- window.cookieStorage = (new (function(){
- var maxage = 60*60*24*1000;
- var path = '/';
- var cookie = getCookie();
- function getCookie(){
- var cookie = {};
- var all = document.cookie;
- if(all === "")
- return cookie;
- var list = all.split("; ");
- for(var i=0; i < list.length; i++){
- var cookies = list[i];
- var p = cookies.indexOf("=");
- var name = cookies.substring(0,p);
- var value = cookies.substring(p+1);
- value = decodeURIComponent(value);
- cookie[name] = value;
- }
- return cookie;
- }
- var keys = [];
- for(var key in cookie)
- keys.push(key);
- this.length = keys.length;
- this.key = function(n){
- if(n<0 || n >= keys.length)
- return null;
- return keys[n];
- };
- this.setItem = function(key, value){
- if(! (key in cookie)){
- keys.push(key);
- this.length++;
- }
- cookie[key] = value;
- var cookies = key + "=" +encodeURIComponent(value);
- if(maxage)
- cookies += "; max-age=" + maxage;
- if(path)
- cookies += "; path=" + path;
- document.cookie = cookies;
- };
- this.getItem = function(name){
- return cookie[name] || null;
- };
- this.removeItem = function(key){
- if(!(key in cookie))
- return;
- delete cookie[key];
- for(var i=0; i<keys.length; i++){
- if(keys[i] === key){
- keys.splice(i, 1);
- break;
- }
- }
- this.length--;
- document.cookie = key + "=; max-age=0";
- };
- this.clear = function(){
- for(var i=0; i<keys.length; i++)
- document.cookie = keys[i] + "; max-age=0";
- cookie = {};
- keys = [];
- this.length = 0;
- };
- })());
有了上面的cookieStorage函数,那就好办了,只需 把localStorage跟cookieStorage整合在一起 就完美了。
那么开始动手,新建一个myStorage.js文件,把上面的cookieStorage代码Copy进去,然后再开始写以下代码:
( myStorage.js )
- //本地存储,localStorage类没有存储空间的限制,而cookieStorage有存储大小限制
- //在不支持localStorage的情况下会自动切换为cookieStorage
- window.myStorage = (new (function(){
- var storage; //声明一个变量,用于确定使用哪个本地存储函数
- if(window.localStorage){
- storage = localStorage; //当localStorage存在,使用H5方式
- }
- else{
- storage = cookieStorage; //当localStorage不存在,使用兼容方式
- }
- this.setItem = function(key, value){
- storage.setItem(key, value);
- };
- this.getItem = function(name){
- return storage.getItem(name);
- };
- this.removeItem = function(key){
- storage.removeItem(key);
- };
- this.clear = function(){
- storage.clear();
- };
- })());
上面的代码是myStorage.js,你可以直接Copy。把做好的myStorage.js文件引入到HTML文档后,用法就是跟localStorage的函数一样,不信你试试:
(1)myStorage.setItem(键名,键值)
在本地客户端存储一个字符串类型的数据,其中,第一个参数“键名”代表了该数据的标识符,而第二个参数“键值”为该数据本身。如:
myStorage.setItem("coffeeType", "mocha"); //存储键名为coffeeType和键值为mocha的数据到本地myStorage.setItem("coffeePrice", "28"); //有了上一句做参考,这句意思你该理解了吧
(2)myStorage.getItem(键名)
读取已存储在本地的数据,通过键名作为参数读取出对应键名的数据。如:
var data = myStorage.getItem("coffeeType"); //读取对应键名为coffeeType的数据(3)myStorage.removeItem(键名)
移除已存储在本地的数据,通过键名作为参数删除对应键名的数据。如:
myStorage.removeItem("coffeeType"); //从本地存储中移除键名为coffeeType的数据(4)myStorage.clear()
移除本地存储所有数据。如:
myStorage.clear(); //保存着的"coffeePrice/28"键/值对也被移除了,所有本地数据拜拜仔细一看,你会发现,localStorage中的local改成了my变成了myStorage,这个不得了啊,当客户的使用环境不支持H5的时候,这个小小的变脸"my"可是会切换成cookie,有那么点可能救你一命。不过为了更加保命,你可以造多一个轮子,当客户环境不支持H5又不支持cookie时,轮子会自动把数据上传到服务器来保存;如果客户支持cookie,但是有定期清除的习惯,那么你可以做一个轮子来控制cookie时间,并在有限时间内把数据备份到服务,等等等等,看你怎么发挥吧。
H5本地存储中,除了包含了localStorage和sessionStorage的Web Storage外,还有一个小众的Web SQL,请看下文。
三、Web SQL教程
1、概述:
H5的本地存储中,其实localStorage并不算是很强大的存储,而Web SQL Database才是牛逼的存在,在浏览器或客户端直接可以实现一个本地的数据库应用,比如做一个个人的备忘录啊,注意,是个人,为什么?因为世面上只有主流的浏览器实现了WebSQL功能,很多非主流并不兼容WebSQL,并且,所谓的主流只是编程开发人员眼中的主流,如果是用户平时自己使用的那些乱七八糟的浏览器,WebSQL简直是灾难啊!!!另外,浏览器间对WebSQL的支持并非为规范,因为这个规范几年前被官方放弃了。还有一个WebSQL不能够广泛使用的原因是,大量前端工程师不懂数据库也没有上进心或好奇心或空闲时间去研究和学会应用WebSQL,导致了开发人员逃避WebSQL和用户对WebSQL没有使用习惯和各类客户端对WebSQL兼容性参差不齐等现象,是WebSQL不能够像服务器端的数据库那么广泛应用的主要原因。
吐槽归吐槽,教程还是要写的。先说学习口诀:“一个语言,三个函数”。
2、口诀:
(1)一个语言:不管是WebSQL还是MySQL,SQL的语法必须掌握。SQL是一个结构化查询语言,说白就是用来查询数据的语言中是最自然、最简洁的。
(2)三个函数:分别是:A. openDatabase 创建或打开一个本地的数据库对象
B. executeSql 执行SQL语句,在回调函数的参数中获取执行结果
C. transaction 处理事务,当一条语句执行失败的时候,之前的执行全部失效
3、SQL:
(1)概述:
以下只是把每个功能对应的最基本的SQL语句过一遍。如果不会SQL的,仅做简单语法参考,有兴趣的童鞋需要找资料系统性地全面学习。(2)创建数据表:
创建具有多个列,每个列用于存放可不同类型的数据。有列自然有行的概念,行代表一组数据,每组数据是当行对应的多个列的数据集合。
CREATE TABLE IF NOT EXISTS 表名(列名称1 数据类型, 列名称2 数据类型, 列名称N 数据类型)(3)查询数据:
从某表中查询某行某列的数据或查询表中所有元素。
SELECT 列名称1,列名称2,列名称3 FROM 表名称 WHERE 某列名 = 某值(4)插入数据:
向某表中插入行数据,行中每个值对应列名。
INSERT INTO 表名(列名称1, 列名称2, 列名称N) VALUES (值1, 值2, 值N)(5)更新数据:
更新某行中列的值。
UPDATE 表名 SET 列名称1=新值, 列名称2=新值, 列名称N=新值 WHERE 某列名 = 某值(6)删除数据:
删除某行,否则删除所有数据。
DELETE FROM 表名 WHERE 列名称 = 值
4、Web SQL本地存储的三个函数:
(1)openDatabase (数据库名字, 数据库版本号, 显示名字, 数据库保存数据的大小, 回调函数(可选))
不过是否一脸懵逼,好奇怪是不是,参数还要写版本号,还有,显示名字什么鬼?不过,经过测试后,我都是直接这样写就是了(大小请自己定),如下:
var db = openDatabase("MyDatabase", "", "My Database", 1024*1024);(2)executeSql(查询字符串, 用以替换查询字符串中问号的参数, 执行成功回调函数(可选), 执行失败回调函数(可选))
参数一自然是SQL语句,其中值数据可用?代替;参数二是SQL语句中?对应的值(很像PDO的预处理)。注意,executeSql不能单独使用,需要在事务transaction函数下使用。例子如下:
executeSql("CREATE TABLE IF NOT EXISTS MyData(name TEXT,message TEXT,time INTEGER)");(3)transaction(包含事务内容的一个方法, 执行成功回调函数(可选), 执行失败回调函数(可选))
事务内容为一个或多个executeSql函数构成。这个函数很难用语言表达,所以请看例子:
db.transaction(function(tx){tx.executeSql("CREATE TABLE IF NOT EXISTS MyData(name TEXT,message TEXT,time INTEGER)", [], function(){ alert("create ok")});
tx.executeSql("SELECT * FROM MyData", [], function(){alert("select ok")});
tx.executeSql("DROP TABLE IF EXISTS MyData", [], function(){alert("drop ok")});
});
5、Web SQL的使用:
程序员最喜欢做的事情之一就是封装,将代码封装成自己喜欢的样子,然后在需要用到时会感谢当年封装好的类或函数不需要再造轮子就直接修改着用。所以,我也不例外,把openDatabase、executeSql、transaction三个核心函数封装成一个类。注意,这个类只是实现基本的功能,并且查询我没有写条件查询而是直接查询全部结果。对封装Web SQL有兴趣的童鞋可以研究一下以下代码:
(没兴趣的请忽略,跳到第四条:H5本地存储的框架程序)
( webSQL.js )
- //回调函数,需要被赋值成函数,初始化为null
- var webSQL_create_handle = null;
- var webSQL_insert_handle = null;
- var webSQL_update_handle = null;
- var webSQL_delete_handle = null;
- var webSQL_select_handle = null;
- var webSQL_drop_handle = null;
- //连接数据库(数据库名,数据大小)
- function webSQL(database="MyDatabase", datasize=1024*1024){
- this.db = openDatabase(database, "", "My Database", datasize);
- }
- webSQL.prototype={
- //作为webSQL的原型
- constructor: webSQL,
- //创建表,参数为表名和列名
- create : function(table, allcol){
- var col = "";
- for(var i=0; i<allcol.length; i++){
- col += allcol[i];
- if(i !== allcol.length-1){
- col += ",";
- }
- }
- var sql = "CREATE TABLE IF NOT EXISTS "+table+"("+col+")";
- this.db.transaction(function(tx){
- tx.executeSql(sql,
- [],
- function(tx,rs){
- console.log(tx,"创建表成功!");
- if(webSQL_create_handle && typeof(webSQL_create_handle)=="function"){
- webSQL_create_handle();
- }
- },
- function(tx,error){
- console.log(error,"创建表失败!");
- }
- );
- });
- },
- //删除表,参数为表名
- drop : function(table){
- var sql = "DROP TABLE IF EXISTS "+table;
- this.db.transaction(function(tx){
- tx.executeSql(sql,
- [],
- function(tx,rs){
- console.log(tx,"删除表成功!");
- if(webSQL_drop_handle && typeof(webSQL_drop_handle)=="function"){
- webSQL_drop_handle();
- }
- },
- function(tx,error){
- console.log(error,"删除表失败!");
- }
- );
- });
- },
- //插入数据,表名,列名,对应列值
- insert : function(tableName, colNameArray, colValueArray){
- var allColName = "";
- var quesMark = "";
- for(var i=0; i<colNameArray.length; i++){
- if(colNameArray[i]){
- allColName += colNameArray[i];
- quesMark += "?";
- if(i !== colNameArray.length-1){
- allColName += ",";
- quesMark += ",";
- }
- }
- }
- var sql = "INSERT INTO "+tableName+"("+allColName+") VALUES ("+quesMark+")";
- this.db.transaction(function(tx){
- tx.executeSql(
- sql,
- colValueArray,
- function(tx,rs){
- console.log(tx,"插入数据成功!");
- if(webSQL_insert_handle && typeof(webSQL_insert_handle)=="function"){
- webSQL_insert_handle();
- }
- },
- function(tx,error){
- console.log(error,"插入数据失败!");
- }
- );
- });
- },
- //更新数据,表名,列名,列值,条件列名,条件列值,条件关系,是否通配
- update : function(tableName, colNameArray, colValueArray, whereColName=null, whereColValue=null, relation="&&", equal="="){
- var colAndValue = "";
- for(var i=0; i<colNameArray.length; i++){
- if(colNameArray[i]){
- colAndValue += (colNameArray[i] + "=?");
- if(i !== colNameArray.length-1){
- colAndValue += ",";
- }
- }
- }
- var whereSyntax = "";
- if(whereColName){
- for(var j=0; j<whereColName.length; j++){
- if(whereColName[j]){
- if(j === 0){
- whereSyntax += " WHERE ";
- }
- whereSyntax += (whereColName[j] + "" + equal + "?");
- if(j !== whereColName.length-1){
- whereSyntax += (" "+relation+" ");
- }
- }
- }
- }
- var fanalArray = new Array();
- for(var m=0; m<colValueArray.length; m++){
- if(colValueArray[m]){
- fanalArray.push(colValueArray[m]);
- }
- }
- if(whereColValue){
- for(var n=0; n<whereColValue.length; n++){
- if(whereColValue[n]){
- fanalArray.push(whereColValue[n]);
- }
- }
- }
- var sql = "UPDATE "+tableName+" SET "+colAndValue+""+whereSyntax;
- this.db.transaction(function(tx){
- tx.executeSql(
- sql,
- fanalArray,
- function(tx,rs){
- console.log(tx,"更新数据成功");
- if(webSQL_update_handle && typeof(webSQL_update_handle)=="function"){
- webSQL_update_handle();
- }
- },
- function(tx,error){
- console.log(error,"更新数据失败!");
- }
- );
- });
- },
- //删除数据,表名,条件列名,条件列值,条件关系,是否通配
- delete : function(tableName, whereColName=null, whereColValue=null, relation="&&", equal="="){
- var whereSyntax = "";
- if(whereColName){
- for(var j=0; j<whereColName.length; j++){
- if(whereColName[j]){
- if(j === 0){
- whereSyntax += " WHERE ";
- }
- whereSyntax += (whereColName[j] + "" + equal + "?");
- if(j !== whereColName.length-1){
- whereSyntax += (" "+relation+" ");
- }
- }
- }
- }
- var fanalColValue = new Array();
- for(var n=0; n<whereColValue.length; n++){
- &nbs