前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Ionic 开发之 Ionic Storage 详解

Ionic 开发之 Ionic Storage 详解

作者头像
阿宝哥
发布2019-11-06 11:52:35
3.7K0
发布2019-11-06 11:52:35
举报
文章被收录于专栏:全栈修仙之路全栈修仙之路

Ionic Storage 是一款基于 localForage 用于 Ionic 应用程序的简单 “键-值” 存储模块,支持 SQLite 开箱即用。该工具可以根据平台自动选择最佳的存储引擎,而不用用户关系具体的使用细节。模块内存储引擎的默认选择顺序是 SQLite,IndexedDB,WebSQL 和 LocalStorage。

在原生应用程序环境中运行时,存储方式会优先使用 SQLite 的原因,是因为它最稳定和最广泛使用的文件数据之一,并且避免了诸如 localStorage 和 IndexedDB 之类的一些陷阱,比如在低磁盘空间的情况下会自动清理数据。在实际开发中,如果你想执行任意 SQL 查询,你可以直接使用 Ionic Native SQLite 插件。

接下来,我们先来介绍一下 Ionic Storage 的安转与使用。

安装与使用

首先,如果你想使用 SQLite,请先安装 cordova-sqlite-storage 插件:

代码语言:javascript
复制
$ ionic cordova plugin add cordova-sqlite-storage

接下来,安装 @Ionic/storage:

代码语言:javascript
复制
$ npm install --save @ionic/storage

然后,导入 IonicStorageModule 并把它添加到根模块 NgModule 的 imports 列表中:

代码语言:javascript
复制
import { IonicStorageModule } from '@ionic/storage';

@NgModule({
  declarations: [
    // ...
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp),
    IonicStorageModule.forRoot()
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    // ...
  ],
  providers: [
    // ...
  ]
})
export class AppModule {}

之后,你就可以在页面或组件中注入 Storage 服务:

代码语言:javascript
复制
import { Component } from '@angular/core';

import { NavController } from 'ionic-angular';

import { Storage } from '@ionic/storage';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

  constructor(
    public navCtrl: NavController, 
    public storage: Storage) {
  }
}

为了确保进行数据操作时,存储系统已经初始化完成。你可以在使用前调用 Storage.ready() 方法,不过该方法仅在 1.1.7 以上的版本才支持:

代码语言:javascript
复制
this.storage.ready().then((db) => {
});

若需要保存数据,则可以调用 set(key, value) 方法:

代码语言:javascript
复制
this.storage.set('name', 'semlinker');

若想要获取上面已存储的 name 信息,你可以调用 get(key) 方法:

代码语言:javascript
复制
this.storage.get('name').then((name) => {
  console.log('Me: Hey, ' + name + '! You have a very nice name.');
  console.log('You: Thanks! I got it for my birthday.');
});

当然,要移除已存储的项目,可以使用 remove(key) 方法:

代码语言:javascript
复制
this.storage.remove('name').then(() => {
  console.log('Name item has been removed');
});

下面我们继续来介绍如何配置 IonicStorageModule 模块。

配置 Storage

你可以使用特定的存储引擎优先级配置存储引擎,也可以将自定义配置项配置为 localForage。更多的选项,请参阅 localForage 配置文档:https://github.com/localForage/localForage#configuration

注意:任何自定义配置将与默认配置合并

代码语言:javascript
复制
import { IonicStorageModule } from '@ionic/storage';

@NgModule({
  declarations: [...],
  imports: [
    IonicStorageModule.forRoot({
      name: '__mydb',
      driverOrder: ['indexeddb', 'sqlite', 'websql']
    })
  ],
  bootstrap: [...],
  entryComponents: [...],
   providers: [...]
})
export class AppModule { }

IonicStorageModule 源码分析

IonicStorageModule 定义
代码语言:javascript
复制
import { NgModule, ModuleWithProviders } from '@angular/core';
import {
  getDefaultConfig,
  provideStorage,
  Storage,
  StorageConfig,
  StorageConfigToken
} from './storage';

export { StorageConfig, StorageConfigToken, Storage };

@NgModule()
export class IonicStorageModule {
  static forRoot(storageConfig: StorageConfig = null): ModuleWithProviders {
    return {
      ngModule: IonicStorageModule,
      providers: [
        { provide: StorageConfigToken, useValue: storageConfig },
        {
          provide: Storage,
          useFactory: provideStorage,
          deps: [StorageConfigToken]
        }
      ]
    };
  }
}

在前面的配置章节,我们通过调用 forRoot() 方法进行初始化操作:

代码语言:javascript
复制
IonicStorageModule.forRoot({
  name: '__mydb',
  driverOrder: ['indexeddb', 'sqlite', 'websql']
})

storageConfig 对象除了包含 name 和 driverOrder 属性外,还支持其它的属性,StorageConfig 接口的定义如下:

代码语言:javascript
复制
export interface StorageConfig {
  name?: string;
  version?: number;
  size?: number;
  storeName?: string;
  description?: string;
  driverOrder?: string[]; // 存储引擎的应用顺序
  dbKey?: string;
}

在 IonicStorageModule 模块内,配置了两个 provider:

代码语言:javascript
复制
providers: [
  { provide: StorageConfigToken, useValue: storageConfig },
  {
    provide: Storage,
    useFactory: provideStorage,
    deps: [StorageConfigToken]
  }
]

第一个 provider 使用 useValue 的方式进行注册,这里 StorageConfigToken 的定义如下:

代码语言:javascript
复制
export const StorageConfigToken = new InjectionToken<any>(
  'STORAGE_CONFIG_TOKEN'
);

第二个 provider 使用 useFactory 的方式进行注册,对应的工厂函数 provideStorage 定义如下:

代码语言:javascript
复制
export function provideStorage(storageConfig: StorageConfig): Storage {
  const config = !!storageConfig ? storageConfig : getDefaultConfig();
  return new Storage(config);
}

provideStorage 函数内部,会先判断 storageConfig 是否有效,如果无效的时候,会使用通过调用 getDefaultConfig() 方法获取默认的配置:

代码语言:javascript
复制
export function getDefaultConfig() {
  return {
    name: '_ionicstorage',
    storeName: '_ionickv',
    dbKey: '_ionickey',
    driverOrder: ['sqlite', 'indexeddb', 'websql', 'localstorage']
  };
}

下面我们来分析一下最核心的 Storage 类。

Storage 类

Storage 构造函数
代码语言:javascript
复制
import { Injectable, InjectionToken, Optional } from '@angular/core';

import LocalForage from 'localforage';

import CordovaSQLiteDriver from 'localforage-cordovasqlitedriver';

export class Storage {
  private _dbPromise: Promise<LocalForage>;
  private _driver: string = null; // 

  constructor(config: StorageConfig) {
    this._dbPromise = new Promise((resolve, reject) => {
      let db: LocalForage;

      const defaultConfig = getDefaultConfig(); // 获取默认配置
      const actualConfig = Object.assign(defaultConfig, config || {}); // 合并配置

      LocalForage.defineDriver(CordovaSQLiteDriver)
        .then(() => { 
          db = LocalForage.createInstance(actualConfig); // 创建db实例
        })
        .then(() =>
          db.setDriver(this._getDriverOrder(actualConfig.driverOrder)) // 设置数据库驱动
        )
        .then(() => {
          this._driver = db.driver(); // 返回最终使用的驱动类型
          resolve(db);
        })
        .catch(reason => reject(reason));
    });
  }
}

上面代码中,在调用 db.setDriver() 方法时,会调用内部的 _getDriverOrder() 方法转换成相应的驱动:

代码语言:javascript
复制
private _getDriverOrder(driverOrder) {
    return driverOrder.map(driver => {
      switch (driver) {
        case 'sqlite':
          return CordovaSQLiteDriver._driver;
        case 'indexeddb':
          return LocalForage.INDEXEDDB;
        case 'websql':
          return LocalForage.WEBSQL;
        case 'localstorage':
          return LocalForage.LOCALSTORAGE;
      }
    });
}

// https://github.com/localForage/localForage/blob/master/src/localforage.js
const DefaultDrivers = {
    INDEXEDDB: idbDriver,
    WEBSQL: websqlDriver,
    LOCALSTORAGE: localstorageDriver
};
Storage 成员方法

在继续分析之前,我们先来大致浏览一下 Storage 类中定义的成员方法:

  • driver() —— 返回 string 或 null,表示正在使用驱动的名称;
  • ready() —— 返回 Promise<LocalForage> 对象,当存储初始化完成后会进入 resolved 状态;
  • get(key) —— 获取与给定键相关联的值,返回 Promise 对象;
  • set(key, value) —— 设置给定键的值,返回 Promise 对象;
  • remove(key) —— 删除与此键关联的值,返回 Promise 对象;
  • clear() —— 清除整个键值存储,返回 Promise 对象;
  • length() —— 获取已存储对象的个数,返回 Promise 对象;
  • keys() —— 返回用存储中的所有键,返回 Promise 对象;
  • forEach(iteratorCallback) —— 迭代每个键值对,返回 Promise 对象:
    • iteratorCallback —— (value, key, iterationNumber)

driver() 和 ready() 方法的实现很简单:

代码语言:javascript
复制
get driver(): string | null { // 私有属性 _driver getter 方法
  return this._driver;
}

ready(): Promise<LocalForage> {
  return this._dbPromise; //  _dbPromise: Promise<LocalForage>;
}

下面来看一下我们常用的 get、set 和 remove 等方法:

代码语言:javascript
复制
// 获取与给定键相关联的值,返回 Promise 对象
get(key: string): Promise<any> {
  return this._dbPromise.then(db => db.getItem(key));
}

// 设置给定键的值,返回 Promise 对象
set(key: string, value: any): Promise<any> {
  return this._dbPromise.then(db => db.setItem(key, value));
}

// 删除与此键关联的值,返回 Promise 对象
remove(key: string): Promise<any> {
  return this._dbPromise.then(db => db.removeItem(key));
}

// 清除整个键值存储,返回 Promise 对象
clear(): Promise<void> {
  return this._dbPromise.then(db => db.clear());
}

最后我们来看一下剩下的三个方法:

代码语言:javascript
复制
// 获取已存储项的个数,返回 Promise 对象
length(): Promise<number> {
  return this._dbPromise.then(db => db.length());
}

// 返回用存储中的所有键,返回 Promise 对象
keys(): Promise<string[]> {
  return this._dbPromise.then(db => db.keys());
}

// 迭代每个键值对,返回 Promise 对象
forEach(
    iteratorCallback: (value: any, key: string, iterationNumber: Number) => any
  ): Promise<void> {
    return this._dbPromise.then(db => db.iterate(iteratorCallback));
}

总结

其实分析完 IonicStorageModule 模块,我们会发现它的内部实现并不复杂。它只是对 localForage 的 API 进行简单的封装,实际的存储功能还是交由 localForage 来完成,感兴趣的小伙伴可以研究一下。实际的开发过程中,在数据存储时,我们可能还会涉及数据响应式、数据加密、数据压缩、数据迁移和备份,有上述需求的同学,可以了解一下 rxdb 这个库。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018/09/06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 安装与使用
  • 配置 Storage
  • IonicStorageModule 源码分析
    • IonicStorageModule 定义
    • Storage 类
      • Storage 构造函数
        • Storage 成员方法
        • 总结
        相关产品与服务
        数据保险箱
        数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档