《JS原理、方法与实践》- 本地存储

简介: 《JS原理、方法与实践》- 本地存储

本地存储就是指在浏览器中存储数据,是相对于服务器中存储数据来说的。

#### 1. 本地存储的分类

浏览器的本地存储主要包括4种方式: Cookie、Storage、SQL数据库和IndexedDB。

###### Cookie

Cookie提出来得最早,其最初得设计目的只是为了保存用户得登录信息,所以并不适合保存大量数据。Cookie容量小,将保存得数据拼接成字符串得形式跟程序进行操作。没打开一个网址,浏览器都会将该网站下得所有Cookie数据全部传到服务器端,因为其最初得设计目的只是用来保护用户的登录信息(例如SessionId)。

###### Storage

Sotrage是HTML5种用于存储本地数据的对象,因为其设计目的就是存储本地数据,所以其操作比Cookie灵活得多。Storage容量比Cookie大,可以直接按照键值对存储数据并使用健名获取数据。Storage分为sessionStorage和localStorage,前者在浏览器关闭后就会被清除,后者会一直存储在浏览器中,除非人为或代码清除。

###### SQL数据库

SQL数据库在浏览器中封装了一个小型得SQLLite数据库,但是从前端架构上并不适合使用。例如:JS对象得属性随时都可能发生改变,可能增删,也可能修改数据类型,但是SQL数据库要求在对数据操作之前就先确定好数据的结构。

###### IndexedDB

IndexedDB属于一种noSQL数据库,是非结构化的,它所保存的数据记录跟JS的对象一样可以随时变化属性(字段)。与Storage相比,它可以保存更多的数据,而且还可以使用数据库的索引、事务等相关概念,但是使用起来要比Storage复杂不少。

#### 2. Storage存储

Storage是window对象的一个function类型的属性对象,但是这个function对象比较特别,它既不可以当作方法来执行,也不可以用来创建实例对象,JS引擎会默认为创建两个Storage的实例对象,直接调用就可以了。

JS引擎创建的两个Storage实例对象分别是sessionStorage和localStorage,前者用于暂时保存,浏览器关闭后数据就会丢失;后者用于长久保存,及时关闭浏览器数据也不会丢失。

Storage.prototype:

![](https://upload-images.jianshu.io/upload_images/2789632-7e5301bf87bd350e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

操作数据的方法有:

* getItem(key)

* setItem(key, value)

* removeItem(key)

* clear()

在控制台利用session storage setItem设置一个值后可以在Application的Session Storage查看:

![](https://upload-images.jianshu.io/upload_images/2789632-1213a9f5a1a7fe05.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![](https://upload-images.jianshu.io/upload_images/2789632-2c54909e644d3e67.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

localStorage与sessionStorage在方法上使用相同,不同之处就在存储后数据清除方式不同。

#### 3. StorageEvent

Storage实例对象修改数据的时候会触发一个StorageEvent事件,这个事件有些特别,当事件触发后,消息会发送到打开的所有当前网站的其他页面中(当然要在同一个浏览器中),至于是否会发送给当前页面,不同的浏览器又不不同的处理方法。(个人测试以下几个浏览器行为均相同,不发送给当前页面:谷歌浏览器(85.0.4183.83)、(Microsoft Edge 44.18362.449.0)、Firefox(78.0.2 (64 位))、双核浏览器(版本 1.0.4.2))

StorageEvent.prototype:

![](https://upload-images.jianshu.io/upload_images/2789632-b34a4a26a0950a80.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

代码示例:

```

<!DOCTYPE html>

<html lang="en">

<head>

   <meta charset="UTF-8">

   <meta name="viewport" content="width=device-width, initial-scale=1.0">

   <title>Document</title>

</head>

<body>

   <input value="Test Storage" type="button" id='button'/>

   <script>

       const btn = document.querySelector('#button');

       btn.onclick = () => {

           localStorage.setItem('testKey', 'testValue1');    

       }

       window.addEventListener('storage', function(e){

           console.log(`key: ${e.key}`);

           console.log(`oldValue: ${e.oldValue}`);

           console.log(`newValue: ${e.newValue}`);

           console.log(`url: ${e.url}`);

           console.log(`is localStorage: ${e.storageArea === localStorage}`);

       });

   </script>

</body>

</html>

```

![测试结果](https://upload-images.jianshu.io/upload_images/2789632-2767057896e82734.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

#### 4. IndexedDB 数据库简介

IndexedDB数据库是一种noSQL数据库,但是其所用到的概念大部分跟SQL数据库类似,当然也存在不完全相同的地方。不同之处仅在于IndexedDB数据库不是将对象转换为记录,然后再保存到表里面,而是直接保存的对象,它是通过“ID->数据对象”这种模式来保存的。

###### IndexedDB数据库操作

跟IndexedDB数据库相关的对象主要包括11个对象:IDBFactory、IDBDatabase、IDBObjectStore、IDBIndex、IDBKeyRange、IDBCursor、IDBCursorWithValue、IDBTransaction、IDBVersionChangeEvent、IDBOpenDBRequest和IDBRequest。

这11个对象都是window的function类型的属性对象,使用时都使用其示例,但不需要用户自己使用new来创建,在需要的时候就可以自动获取。11个对象可以分为4类:数据库和ObjecStore相关对象、数据相关对象、查询相关对象和辅助对象。气筒辅助对象包括IDBVersionChangeEvent、IDBOpenDBRequest和IDBRequest。

###### 数据库和ObjectStore相关操作

1. 创建/打开数据库

IndexedDB数据库的操作是基于事件的,或者说是异步处理的。open方法返回的是一个IDBOpenDBRequest的实力对象,常用属性有onsuccess、onerror、onupgradeneeded,分别表示打开成功、打开失败和数据库版本升级。

语法:

```

const db_worker = indexedDB.open('dbName', dbVersion);

```

示例:

```

   <script>

       const workerDBRequest = indexedDB.open('database', 1);

       workerDBRequest.onerror = function(event) {

           console.log('打开数据库失败');

       }

       workerDBRequest.onsuccess = function(event) {

           db_worker = workerDBRequest.result; // 也可以使用event.target.result

           console.log('打开成功');

       }

   </script>

```

![](https://upload-images.jianshu.io/upload_images/2789632-c63213b7c6c51b0d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![open返回对象包含的属性、方法](https://upload-images.jianshu.io/upload_images/2789632-dc17577198950938.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![](https://upload-images.jianshu.io/upload_images/2789632-4617aeca8edc7681.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

2. 创建ObjectStore

通过上图,我们可以看出, indexedDB.open(),返回的是一个IDBOpenDBRequest对象,IDBOpenDBRequest的result是一个IDBDatabase对象,有了这个对象,就可以创建ObjectStore了。这个result包含的属性方法如下:

![](https://upload-images.jianshu.io/upload_images/2789632-8934023e7ee00d2e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

createObjectStore、deleteObjectStore分别用于创建和删除ObjectStore。

###### 注意: createObjectStore、deleteObjectStore只能在onupgradeneeded方法中执行。

createObjectStore方法有两个参数,第一个参数是所要创建的ObjectSore的名字,第二个参数是一个对象,用于描述ID,该对象可以有两个属性:keyPath和autoIncrement,前者用来指定一个对象中的属性用作ID,后者若为true则会在保存数据时用Generator生成一个ID,但是这里需要自己创建一个Generator。

示例如下:

```

<script>

       const workerDBRequest = indexedDB.open('worker', 2);

       workerDBRequest.onerror = function(event) {

           console.log('打开数据库失败');

       }

       workerDBRequest.onsuccess = function(event) {

           db_worker = workerDBRequest.result; // 也可以使用event.target.result

           console.log('打开数据库成功');

       }

       workerDBRequest.onupgradeneeded = function(e) {

           var db = workerDBRequest.result;

           db.createObjectStore('category', {});

           console.log(`${db.name} 的版本号修改为了${db.version}`);

       }

   </script>

```

###### 数据操作相关

数据相关操作除了前面介绍的之外,主要还要用到表示事务的IDBTransaction对象。

数据库(IDBDatabse的实例对象)的绝大多数操作都需要用事务来完成,数据库实例对象包含一个transaction方法属性(其实时IDBDatabase.prototype的属性),调用该属性方法就可以创建事务。transaction方法有两个参数,第一个参数用于指定要操作的ObjectStore,可以是一个,也可以是多个,多个采用数组传递;第二个参数指定事务的类型,可以是readonly(默认)或者readwrite,分别表示创建只读或读写事务。

###### IDBTransaction实例所包含的属性:

["objectStoreNames", "mode", "db", "error", "onabort", "oncomplete", "onerror", "objectStore", "commit", "abort", "durability", "constructor"]

![](https://upload-images.jianshu.io/upload_images/2789632-3663c02bd21de5e0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

###### IDBObjectStore实例包含的属性:

["name", "keyPath", "indexNames", "transaction", "autoIncrement", "put", "add", "delete", "clear", "get", "getKey", "getAll", "getAllKeys", "count", "openCursor", "openKeyCursor", "index", "createIndex", "deleteIndex", "constructor"]

![](https://upload-images.jianshu.io/upload_images/2789632-3614f8959dc9bb27.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

###### 增加数据

示例:

```

<script>

       const workerDBRequest = indexedDB.open('worker', 3);

       workerDBRequest.onerror = function(event) {

           console.log('打开数据库失败');

       }

       workerDBRequest.onupgradeneeded = function(e) {

           var db = workerDBRequest.result;

           db.createObjectStore('category', {keyPath: 'id'});

           console.log(`${db.name} 的版本号修改为了${db.version}`);

       }

       workerDBRequest.onsuccess = function(event) {

           db_worker = workerDBRequest.result; // 也可以使用event.target.result

           console.log('打开数据库成功');

           workerDBOpenSuccess();

       }

     

       function workerDBOpenSuccess() {

           const tx_category = db_worker.transaction('category', 'readwrite');

           const store_category = tx_category.objectStore('category');

           const category1 = {'id':007, 'name':'zzh', 'age':18};

           const categoryAddRequest = store_category.add(category1);

           categoryAddRequest.onsuccess = function(event) {

               console.log('保存成功');

           }

           categoryAddRequest.onerror = function(evevt) {

               console.log('保存失败');

           }

       }

   </script>

```

![测试结果](https://upload-images.jianshu.io/upload_images/2789632-560cd995c89e0494.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

###### 查询数据

实例:

```

   <script>

       const workerDBRequest = indexedDB.open('worker', 3);

       workerDBRequest.onerror = function(event) {

           console.log('打开数据库失败');

       }

       workerDBRequest.onupgradeneeded = function(e) {

           var db = workerDBRequest.result;

           db.createObjectStore('category', {keyPath: 'id'});

           console.log(`${db.name} 的版本号修改为了${db.version}`);

       }

       workerDBRequest.onsuccess = function(event) {

           db_worker = workerDBRequest.result; // 也可以使用event.target.result

           console.log('打开数据库成功');

           workerDBOpenSuccess();

       }

     

       function workerDBOpenSuccess() {

           const tx_category = db_worker.transaction('category', 'readwrite');

           const store_category = tx_category.objectStore('category');

           const categoryGetRequest = store_category.get(7);

           categoryGetRequest.onsuccess = function (event) {

               console.log(`查询成功: ${JSON.stringify(categoryGetRequest.result)}`);

           }

       }

   </script>

```

![测试结果](https://upload-images.jianshu.io/upload_images/2789632-056c410f69868323.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

查询的方式还有很多种,就像关系型数据库一样,查询可以根据不同的条件筛选数据。

###### 修改数据

```

   <script>

       const workerDBRequest = indexedDB.open('worker', 3);

       workerDBRequest.onerror = function (event) {

           console.log('打开数据库失败');

       }

       workerDBRequest.onupgradeneeded = function (e) {

           var db = workerDBRequest.result;

           db.createObjectStore('category', {

               keyPath: 'id'

           });

           console.log(`${db.name} 的版本号修改为了${db.version}`);

       }

       workerDBRequest.onsuccess = function (event) {

           db_worker = workerDBRequest.result; // 也可以使用event.target.result

           console.log('打开数据库成功');

           workerDBOpenSuccess();

       }

       function workerDBOpenSuccess() {

           const tx_category = db_worker.transaction('category', 'readwrite');

           const store_category = tx_category.objectStore('category');

           const categoryGetRequest = store_category.get(7);

           categoryGetRequest.onsuccess = function (event) {

               console.log(`查询成功: ${JSON.stringify(categoryGetRequest.result)}`);

               let data = event.target.result;

               data.age = 19;

               const categoryPutRequest = store_category.put(data);

               categoryPutRequest.onsuccess = function (event) {

                   console.log('更新成功');

               }

           }

       }

   </script>

```

![控制台输出](https://upload-images.jianshu.io/upload_images/2789632-390560ab17cd5b53.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

IndexedDB可能不会实时更新显示,有时需要刷新后查看,实际值已经修改。

![Application显示](https://upload-images.jianshu.io/upload_images/2789632-8eabb8bbd451179d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

###### 删除数据

示例:

```

<script>

       const workerDBRequest = indexedDB.open('worker', 3);

       workerDBRequest.onerror = function (event) {

           console.log('打开数据库失败');

       }

       workerDBRequest.onupgradeneeded = function (e) {

           var db = workerDBRequest.result;

           db.createObjectStore('category', {

               keyPath: 'id'

           });

           console.log(`${db.name} 的版本号修改为了${db.version}`);

       }

       workerDBRequest.onsuccess = function (event) {

           db_worker = workerDBRequest.result; // 也可以使用event.target.result

           console.log('打开数据库成功');

           workerDBOpenSuccess();

       }

       function workerDBOpenSuccess() {

           const tx_category = db_worker.transaction('category', 'readwrite');

           const store_category = tx_category.objectStore('category');

           const categoryDeleteRequest = store_category.delete(7); // 根据id删除

           categoryDeleteRequest.onsuccess = function(event) {

               console.log('删除成功');

           }

       }

   </script>

```

显示需要刷新后查看。

![测试结果](https://upload-images.jianshu.io/upload_images/2789632-2176996c6507d676.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

IndexedDB的操作还有很多,首先大概理解下noSQL的原理,然后再对这些对象,以及对象可进行的操作进行理解使用。感兴趣的也可以参看MDN详解:https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API/Using_IndexedDB

目录
相关文章
|
1月前
|
JavaScript 前端开发 程序员
前端原生Js批量修改页面元素属性的2个方法
原生 Js 的 getElementsByClassName 和 querySelectorAll 都能获取批量的页面元素,但是它们之间有些细微的差别,稍不注意,就很容易弄错!
|
1月前
|
监控 JavaScript Java
Node.js中内存泄漏的检测方法
检测内存泄漏需要综合运用多种方法,并结合实际的应用场景和代码特点进行分析。及时发现和解决内存泄漏问题,可以提高应用的稳定性和性能,避免潜在的风险和故障。同时,不断学习和掌握内存管理的知识,也是有效预防内存泄漏的重要途径。
135 52
|
1月前
|
缓存 JavaScript UED
js中BOM中的方法
【10月更文挑战第31天】
|
18天前
|
存储 网络架构
Next.js 实战 (四):i18n 国际化的最优方案实践
这篇文章介绍了Next.js国际化方案,作者对比了网上常见的方案并提出了自己的需求:不破坏应用程序的目录结构和路由。文章推荐使用next-intl库来实现国际化,并提供了详细的安装步骤和代码示例。作者实现了国际化切换时不改变路由,并把当前语言的key存储到浏览器cookie中,使得刷新浏览器后语言不会失效。最后,文章总结了这种国际化方案的优势,并提供Github仓库链接供读者参考。
|
1月前
|
缓存 JavaScript 前端开发
JavaScript 与 DOM 交互的基础及进阶技巧,涵盖 DOM 获取、修改、创建、删除元素的方法,事件处理,性能优化及与其他前端技术的结合,助你构建动态交互的网页应用
本文深入讲解了 JavaScript 与 DOM 交互的基础及进阶技巧,涵盖 DOM 获取、修改、创建、删除元素的方法,事件处理,性能优化及与其他前端技术的结合,助你构建动态交互的网页应用。
50 5
|
1月前
|
缓存 监控 JavaScript
Vue.js 框架下的性能优化策略与实践
Vue.js 框架下的性能优化策略与实践
|
1月前
|
缓存 负载均衡 JavaScript
构建高效后端服务:Node.js与Express框架实践
在数字化时代的浪潮中,后端服务的重要性不言而喻。本文将通过深入浅出的方式介绍如何利用Node.js及其强大的Express框架来搭建一个高效的后端服务。我们将从零开始,逐步深入,不仅涉及基础的代码编写,更会探讨如何优化性能和处理高并发场景。无论你是后端新手还是希望提高现有技能的开发者,这篇文章都将为你提供宝贵的知识和启示。
|
1月前
|
缓存 前端开发 JavaScript
JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式
本文深入解析了JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式(Hash路由和History路由)、优点及挑战,并通过实际案例分析,帮助开发者更好地理解和应用这一关键技术,提升用户体验。
75 1
|
1月前
|
监控 JavaScript 算法
深度剖析 Vue.js 响应式原理:从数据劫持到视图更新的全流程详解
本文深入解析Vue.js的响应式机制,从数据劫持到视图更新的全过程,详细讲解了其实现原理和运作流程。
|
1月前
|
JavaScript 前端开发
js中的bind,call,apply方法的区别以及用法
JavaScript中,`bind`、`call`和`apply`均可改变函数的`this`指向并传递参数。其中,`bind`返回一个新函数,不立即执行;`call`和`apply`则立即执行,且`apply`的参数以数组形式传递。三者在改变`this`指向及传参上功能相似,但在执行时机和参数传递方式上有所区别。
29 1