JS进修笔记(第23章)(离线运用与客户端存储1)

所谓Web离线运用,就是在装备不能上网的情况下依旧能够运转的运用。开辟离线Web运用须要几个步骤:
(1)确保运用晓得装备是不是能上网;
(2)运用还必须能接见肯定的资本(图象、JavaScript、CSS等);
(3)必须有一块当地空间用于保留数据,不管可否上网都不阻碍读写。

1、离线检测

HTML5定义了一个navigator.onLine属性,这个属性值为true示意装备能上网,值为false示意装备离线。
零丁运用navigator.onLine属性不能肯定收集是不是连通。即便如此,在请求发作毛病的情况下,检测这个属性依旧是管用的。

if(navigator.onLine) {
    //平常事情
} else {
    //实行离线状况时的使命
}

除了navigator.onLine属性以外,为了更好地肯定收集是不是可用,HTML5还定义了两个事宜:onLine和offline。当收集从离线变成在线或许从在线变成离线时,离别触发这两个事宜。这两个事宜在window对象上触发。

//从离线变成在线
EventUtil.addHandler(window, "online", function() {
    alert("Online");
});
//从如今变成离线
EventUtil.addHandler(window, "offline", function() {
    alert("Offline");
});

为了检测运用是不是离线,在收集加载后,最好先经由过程navigator.onLine获得初始的状况。然后,就是经由过程上述两个事宜来肯定收集衔接状况是不是变化。当上述事宜触发时,navigator.onLine属性的值也会转变。

2、运用缓存

(1)HTML5的运用缓存,或许简称为appache,是特地为开辟离线Web运用而设想的。Appcache就是从浏览器的缓存平分出来的一块缓存区。要想在这个缓存中保留数据,可运用一个形貌文件(manifest file)列出要下载和缓存的资本。

CACHE MANIFEST
  #Comment

  file.js
  file.css

要将形貌文件与页面关联起来,能够在<html>中的manifest属性中指定这个文件的途径,比方

<html manifest="/offline.manifest">

以上代码通知页面,/offline.manifest中包括着形貌文件。

(2)虽然运用缓存的企图是确保离线时资本可用,但也有相应的JavaScript API让我们晓得它都在做什么,这个API的中心是applicationCache对象,这个对象有一个status属性,属性的值是常量,示意运用缓存的以下当前状况。

  • 0:无缓存,即没有与页面相干的运用缓存
  • 1:闲置,即运用缓存未获得更新
  • 2:搜检中,即正在下载形貌文件并搜检更新
  • 3:下载中,即运用缓存正在下载形貌文件中的指定资本
  • 4:更新完成,即运用缓存已更新了资本,而且一切资本都已下载终了,能够经由过程swapCache()来运用了
  • 5:烧毁,即运用缓存的形貌文件已不存在了,因而页面没法再接见缓存。

(3)运用缓存另有许多相干的事宜,示意其状况的转变。以下是这些事宜:

  • checking:在浏览器为运用缓存查找更新时触发
  • error:在搜检更新或下载资本其时期发作毛病时触发
  • noupdate:在搜检形貌文件发明文件无变化时触发
  • downloading:在最先下载运用缓存资本时触发
  • progress:在文件下载运用缓存的过程当中延续不停的触发
  • updateready:在页面新的运用缓存下载终了且能够经由过程swapCache()运用时触发
  • cached:在运用缓存完整可用时触发

平常来说,这些时刻会跟着页面加载按上述递次顺次触发,不过经由过程挪用update()要领也能够手工干涉干与,让运用缓存为搜检更新而触发上述事宜。

applicationCache.update();

update()已一经挪用,运用缓存就会去搜检形貌文件是不是更新(触发checking事宜),然后就像页面方才加载一样,继承实行后续操纵。假如触发了cached事宜,就申明运用缓存已准备就绪,不会再发作其他操纵了。假如触发了updateready事宜,则申明新版本的运用缓存已可用,而此时你须要挪用swapCache()来启用新运用缓存。

Event.addHandler(applicationCache, "updateready", function() {
    applicationCache.swapCache();
});

3、数据存储

3.1 Cookie

HTTP Cookie,一般直接叫做cookie,最初是在客户端用于存储会话信息的。该规范请求服务器对恣意HTTP请求发送Set-Cookies HTTP头作为相应的一部份,个中包括会话信息。比方,这类服务器相应头能够以下:

HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value
Other-header: other-header-value

这个HTTP相应设置以name为称号,以value为值得一个cookie,称号和值在传送时都必须是URL编码的。浏览器会存储如许的会话信息,并在这以后,经由过程为每一个请求增加Cookie HTTP头将信息发送回服务器。

GET /index.html HTTP/1.1 
Cookie: name=value
Other-header: other-header-value

发送回服务器的分外信息能够用于唯一考证客户来自于发送的哪一个请求。

1、限定(绑定在特定域名下;数目限定;尺寸限定)

(1)cookie在性子上是绑定在特定的域名下的。当设定了一个cookie后,再给竖立它的域名发送请求时,都邑包括这个cookie。这个限定确保里存储在cookie中的信息只能让同意的接受者接见,而没法被其他域接见。
(2)每一个域的cookie总数老是有限的,当凌驾单个域名限定以后还要再设置cookie,浏览器就会消灭之前设置的cookie。IE和Opera会删除近来起码运用过的cookie,腾出空间给新设置的cookie。Firefox看上去好像是随机决定要消灭哪一个cookie,所以斟酌cookie限定异常重要,以防止涌现不可预期的效果。
(3)浏览器中关于cookie的尺寸也有限定。尺寸限定影响到一个域下一切的cookie,而并不是每一个cookie零丁限定。假如你尝试竖立凌驾最大尺寸限定的cookie,那末该cookie会被悄无声息地丢掉。

2、cookie的组成

cookie由浏览器保留的一下几块信息组成。(称号、值、域、途径、失效时刻、平安标志)

  • 称号:一个唯一肯定cookie的称号。cookie称号是不辨别大小写的,然则实践中最好将cookie称号看做是辨别大小写的。cookie的称号必须是经由URL编码的。
  • 值:存储在cookie中的字符串值。值必须被URL编码。
  • 域:cookie关于哪一个域是有用的。一切向该域发送的请求都邑包括这个cookie信息。这个值能够包括子域(subdomain,如www.wrox.com),也能够不包括它(如.wrox.com,则关于wrox.com的一切子域都有用)。假如没有明白设定,那末这个域会被认作来自设置cookie的谁人域。
  • 途径:关于指定域中的谁人途径,应当向服务器发送cookie。比方,你能够指定cookie只需从http://www.wrox.com/books/ 中才接见,那末http://www.wrox.com 的页面就不会发送cookie信息,纵然请求都是来自于同一个域的。
  • 失效时刻:示意cookie什么时刻应当被删除的时刻戳(也就是,什么时刻应当住手向服务器发送这个cookie)。默许情况下,浏览器会话完毕时行将一切cookie删除;不过也能够本身设置删除时刻。这个值是个GMT花样的日期,用于指定应当删除cookie的正确时刻。cookie可在浏览器封闭后依旧保留在用户的机械上。假如你设置的失效日期是个之前的时刻,则cookie会被马上删除。
  • 平安标志:指定后,cookie只需在运用SSL衔接的时刻才发送到服务器。比方,cookie信息只能发送给https://www.wrox.com,而 http://www.wrox.com 的请求则不能发送。

每一段信息都作为Set-Cookie头的一部份,运用分号加空格分开每一段,以下例所示:

HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value; expires=Mon, 22-Jan-07 07:10:24 GMT; domain=.wrox.com
Other-header: other-header-value

secure标志是cookie中唯一一个非名值对儿的部份,直接包括一个secure单词。

HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value; domain=.wrox.com; path=/; secure
Other-header: other-header-value

这里竖立了一个关于一切wrox.com的子域和域名下(由path参数指定的)一切页面都是有用的cookie。因为设置了secure标志,这个cookie只能经由过程SSL衔接才传输。

特别要注意,域、途径、失效时刻和secure标志都是服务器给浏览器的指导,以指定什么时刻应当发送cookie。这些参数并不会作为发送到服务器的cookie信息的一部份,只需名值对儿才会被发送。

3、JavaScript中的cookie

JavaScript中处置惩罚cookie有些庞杂,因为BOM的document.cookie属性比较奇特,它会因为运用它的差别而表现出差别的行动。

当用来猎取属性值时,document.cookie返回当前页面可用的(依据cookie的域、途径、失效时刻和平安设置)一切cookie的字符串,一系列由分号离隔的名值对儿。

当用于设置值的时刻,document.cookie属机能够设置为一个新的cookie字符串。这个cookie字符串会被诠释并增加到现有的cookie鸠合中。设置document.cookie并不会掩盖cookie,除非设置cookie的称号已存在。设置cookie的花样以下,和Set-Cookie头中运用的花样一样。

name=value; expires=expiration_time; path=domain_name; secure

这些参数中,只需cookie的名字和值是必须的。如:最好每次设置cookie时都像下面如许运用encodeURI-Component();

document.cookie = encodeURIComponent("name") + "=" + encodeURIComponent("Nicholas");

要给被竖立的cookie指定分外的信息,只需将参数追加到该字符串,和Set-Cookie头中的花样一样,以下所示:

document.cookie = encodeURIComponent("name") + "=" + encodeURIComponent("Nicholas") + "; domain=.wrox.com; path=/";

因为JavaScript中读写cookie不是异常直观,经常须要写一些函数来简化cookie功用。基础的cookie操纵有3种:读取、写入和删除。

一切名字和值都是经由URL编码的,一切必须运用decodeURIComponent()来解码

var CookieUtil = {
    get: function(name){
    //查找cookie名加上等于号的位置。假如找到了,那末运用indexOf()查找该位置以后的第一个分号(示意了该cookie的完毕位置)。假如没有找到分号,则示意该cookie是字符串中的末了一个,则余下的字符串都是cookie的值。该值运用decodeURIComponent()举行解码并末了返回。假如没有发明cookie,则返回null。
        var cookieName = encodeURIComponent(name) + "=",
            cookieStart = document.cookie.indexOf(cookieName),
            cookieValue = null;

        if(cookieStart > -1){
            var cookieEnd = document.cookie.indexOf(";",cookieStart);
            if(cookieEnd == -1){
                cookieEnd = document.cookie.length;
            }
            cookieValue = decodeURIComponent(document.cookie.substring(cookieStart+cookieName.length,cookieEnd));
        }
        return cookieValue;
    },
    
    set: function(name, value, expires, path, domain, secure) {
        var cookieText = encodeURIComponent(name) + "=" + encodeURIComponent(value);

        if(expires instanceof Date) {
            cookieText += "; expires=" + expires.toGMTString();
        }

        if(path) {
            cookieText += "; path=" + path;
        }

        if(domain) {
            cookieText += "; domain=" + domain;
        }

        if(secure) {
            cookieText += "; secure";
        }

        document.cookie = cookieText;
    },
    //没有删除已有cookie的直接要领。所以,须要运用雷同的途径、域和平安选项再次设置cookie,并将失效时刻设置为过去的时刻。
    unset: function (name, path, domain, secure) {
        this.set(name, "", new Date(0), path, domain, secure);
    }
};

然后就能够像下面如许运用上述要领

//设置cookie
CookieUtil.set("name", "Nicholas");
CookieUtil.set("book", "Professional JavaScript");

//读取cookie的值
alert(CookieUtil.get("name")); //"Nicholas"
alert(CookieUtil.get("book")); //"professional JavaScript" 
            
//删除cookie
CookieUtil.unset("name");
CookieUtil.unset("book");

4、子cookie
为了绕开浏览器的单域名下的cookie数限定,一些开辟人员运用了一种称为子cookie(subcookie)的观点。子cookie是寄存在的谁人cookie中的更小段的数据。也就是运用cookie值来存储多个称号值对儿。子cookie最常见的花样以下

name=name1=value1&name2=value2&name3=value3&name4=value4&name5=value5

子cookie平常也以查询字符串否定花样举行花样化。然后这些值能够运用单个cookie举行存储和接见,而非对每一个称号-值对儿运用差别的cookie存储。末了网站或许微博运用程序能够无需到达单域名cookie上限也能够存储越发结构化的数据。

为了更好地操纵子cookie,必须竖立一系列新的要领。子cookie的剖析序列和序列化会因子cookie的希冀用处而略有差别并越发庞杂些。

猎取子cookie的要领有两个:get()和getAll()。个中get()猎取单个子cookie的值,getAll()猎取一切子cookie并将它们放入一个对象中返回,对象的属性为子cookie的称号,对应值为子cookie对应的值。get()要领吸收两个参数:cookie的名字和子cookie的名字。它实在就是挪用getAll()猎取一切的子cookie,然后只返回所需的那一个(假如cookie不存在则返回null)。

get: function (name, subName){
        var subCookies = this.getAll(name);
        if (subCookies){
            return subCookies[subName];
        } else {
            return null;
        }
    },
    
    getAll: function(name){
        var cookieName = encodeURIComponent(name) + "=",
            cookieStart = document.cookie.indexOf(cookieName),
            cookieValue = null,
            cookieEnd,
            subCookies,
            i,
            parts,
            result = {};
            
        if (cookieStart > -1){
            cookieEnd = document.cookie.indexOf(";", cookieStart)
            if (cookieEnd == -1){
                cookieEnd = document.cookie.length;
            }
            cookieValue = document.cookie.substring(cookieStart + cookieName.length, cookieEnd);
            
            if (cookieValue.length > 0){
                subCookies = cookieValue.split("&");
                
                for (i=0, len=subCookies.length; i < len; i++){
                    parts = subCookies[i].split("=");
                    result[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
                }
                return result;
            }  
        } 

        return null;
    }

要设置子cookie,也有两种要领:set()和setAll().为了在同一个cookie中存储多个子cookie,途径、域和secure标志必须一致;针对悉数cookie的失效日期则能够在任何一个零丁的子cookie写入的时刻同时设置。在这个set()要领中,第一步是猎取指定cookie称号对应的一切子cookie。逻辑或操纵符”||”用于当getAll()返回null时将subcookies设置为一个新对象。然后,在subcookies对象上设置好子cookie值并传给setAll()。

set: function (name, subName, value, expires, path, domain, secure) {
    
        var subcookies = this.getAll(name) || {};
        subcookies[subName] = value;
        this.setAll(name, subcookies, expires, path, domain, secure);

    },
    
    setAll: function(name, subcookies, expires, path, domain, secure){
    
        var cookieText = encodeURIComponent(name) + "=",
            subcookieParts = new Array(),
            subName;
        
        for (subName in subcookies){
            if (subName.length > 0 && subcookies.hasOwnProperty(subName)){
                subcookieParts.push(encodeURIComponent(subName) + "=" + encodeURIComponent(subcookies[subName]));
            }
        }
        
        if (subcookieParts.length > 0){
            cookieText += subcookieParts.join("&");
                
            if (expires instanceof Date) {
                cookieText += "; expires=" + expires.toGMTString();
            }
        
            if (path) {
                cookieText += "; path=" + path;
            }
        
            if (domain) {
                cookieText += "; domain=" + domain;
            }
        
            if (secure) {
                cookieText += "; secure";
            }
        } else {
            cookieText += "; expires=" + (new Date(0)).toGMTString();
        }
    
        document.cookie = cookieText;        
    
    }

子cookie的末了一组要领是用于删除子cookie的。一般cookie能够经由过程将失效时刻设置为过去的时刻的要领来删除,然则子cookie不能如许做。为了删除一个子cookie,起首必须猎取包括在某个cookie中的一切子cookie,然后再将余下的子cookie的值保留为cookie的值。

 unset: function (name, subName, path, domain, secure){
        var subcookies = this.getAll(name);
        if (subcookies){
            delete subcookies[subName];
            this.setAll(name, subcookies, null, path, domain, secure);
        }
    },
    
    unsetAll: function(name, path, domain, secure){
        this.setAll(name, null, new Date(0), path, domain, secure);
    }

悉数SubCookieUtil部份代码以下:

var SubCookieUtil = {

    get: function (name, subName){
        var subCookies = this.getAll(name);
        if (subCookies){
            return subCookies[subName];
        } else {
            return null;
        }
    },
    
    getAll: function(name){
        var cookieName = encodeURIComponent(name) + "=",
            cookieStart = document.cookie.indexOf(cookieName),
            cookieValue = null,
            cookieEnd,
            subCookies,
            i,
            parts,
            result = {};
            
        if (cookieStart > -1){
            cookieEnd = document.cookie.indexOf(";", cookieStart)
            if (cookieEnd == -1){
                cookieEnd = document.cookie.length;
            }
            cookieValue = document.cookie.substring(cookieStart + cookieName.length, cookieEnd);
            
            if (cookieValue.length > 0){
                subCookies = cookieValue.split("&");
                
                for (i=0, len=subCookies.length; i < len; i++){
                    parts = subCookies[i].split("=");
                    result[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
                }
    
                return result;
            }  
        } 

        return null;
    },
    
    set: function (name, subName, value, expires, path, domain, secure) {
    
        var subcookies = this.getAll(name) || {};
        subcookies[subName] = value;
        this.setAll(name, subcookies, expires, path, domain, secure);

    },
    
    setAll: function(name, subcookies, expires, path, domain, secure){
    
        var cookieText = encodeURIComponent(name) + "=",
            subcookieParts = new Array(),
            subName;
        
        for (subName in subcookies){
            if (subName.length > 0 && subcookies.hasOwnProperty(subName)){
                subcookieParts.push(encodeURIComponent(subName) + "=" + encodeURIComponent(subcookies[subName]));
            }
        }
        
        if (subcookieParts.length > 0){
            cookieText += subcookieParts.join("&");
                
            if (expires instanceof Date) {
                cookieText += "; expires=" + expires.toGMTString();
            }
        
            if (path) {
                cookieText += "; path=" + path;
            }
        
            if (domain) {
                cookieText += "; domain=" + domain;
            }
        
            if (secure) {
                cookieText += "; secure";
            }
        } else {
            cookieText += "; expires=" + (new Date(0)).toGMTString();
        }
    
        document.cookie = cookieText;        
    
    },
    
    unset: function (name, subName, path, domain, secure){
        var subcookies = this.getAll(name);
        if (subcookies){
            delete subcookies[subName];
            this.setAll(name, subcookies, null, path, domain, secure);
        }
    },
    
    unsetAll: function(name, path, domain, secure){
        this.setAll(name, null, new Date(0), path, domain, secure);
    }

};

能够像下面如许运用上述要领:

 //获得悉数子cookie
 var data = SubCookieUtil.getAll("data");
 alert(data.name);  //"Nicholas"
 alert(data.book);  //"Professional JavaScript"

 //逐一猎取子cookie
 alert(SubCookieUtil.get("data","name")); //"Nicholas"
 alert(SubCookieUtil.get("data","book")); //"Professional JavaScript"

  //设置两个cookie
  SubCookieUtil.set("data", "name", "Nicholas");
  SubCookieUtil.set("data", "book", "Professional JavaScript");

  //设置悉数子cookie和失效日期
  SubCookieUtil.setAll("data",{name:"Nicholas", book:"Professional JavaScript"},new Date("January 1, 2010"));
  //修正名字的值,并修正cookie的失效日期
  SubCookieUtil.set("data", "name", "Michael", new Date("February 1,2010"));

5、关于cookie的思索
另有一类cookie被称为“HTTP专有cookie”。HTTP专有cookie能够从浏览器或许服务器设置,然则只能从服务器端读取。因为一切的cookie都邑由浏览器作为请求头发送,所以在cookie中存储大批信息会影响到特定域请求机能。cookie信息越大,完成对服务器请求的时刻也就越长。只管浏览器对cookie举行了大小限定,不过最好照样尽能够在cookie中少贮存信息,以防止影响机能。

cookie的性子和它的范围使得并不能作为存储大批信息的抱负手腕。
肯定不要在cookie中存储重要和敏感的数据。

3.2 IE用户数据

微软经由过程一个自定义行动引入了耐久化用户数据的观点。要运用耐久化用户数据,起首必须以下所示,运用CSS在某个元素上指定userData行动:

<div style="behavior:url(#default#userDate)" id="dataStore"></div>

一旦该元素运用了userDate行动,那末就能够(1)运用setAttribute()要领在上面保留数据了。为了将数据提交到浏览器缓存中,还必须(2)挪用save()要领并通知它要保留到的数据空间的名字。数据空间名字能够完整恣意,仅用于辨别差别数据集。(3)下一次页面载入以后,能够运用load()要领指定一样的数据空间称号来猎取数据。

var dataStore = document.getElementById("dataStore");
dataStore.setAttribute("name", "Nicholas");
dataStore.setAttribute("book","Professional JavaScript");
datastore.save("BookInfo");  //指定了数据空间的称号为BookInfo
dataStore.load("BookInfo");
alert(dataStore.getAttribute("name")); //"Nicholas"
alert(dataStore.getAttribute("book")); //"Professional JavaScript"

对load()的挪用猎取了BookInfo数据空间中的一切信息,而且使数据能够经由过程元素接见;只需到载入确实完成以后数据方能运用。假如getAttribute()挪用了不存在的称号或许是还没有载入的称号,则返回null。

经由过程removeAttribute()要领明白指定要删除某元素数据,只需指定属性称号。删除以后,必须像下面如许再次挪用save()来提交变动。

dataStore.removeAttribute("name");
dataStore.removeAttribute("book");
dataStore.save("BookInfo");

和cookie一样,IE用户数据并不是平安的,所以不能寄存敏感信息。

3.3 Web存储机制

3.4 IndexedDB

    原文作者:皮卡丘丘丘
    原文地址: https://segmentfault.com/a/1190000018732376
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞