使用Vue构建Ionic混合APP系列教程(四):数据存储

狄楷
2023-12-01

大多数应用程序基本都需要保存一些在应用重新加载时需要的数据。我们经常使用用户设备上的本地存储来实现。当使用Ionic/Angular的时候,我们可以简单的使用Ionic内置的Storage API,并不需要知道背后的原理——Ionic会自动地选择最合适的存储方式。

再说一次,在Vue里我们没有现成的东西可以用,是安装一个库还是自己搭建解决方案全决定于我们自己。

在这篇教程,我们将扩展上篇教程里的Reddit应用,允许我们可以切换多个不同的subreddits方法。我们会存储用户的选择,当重新进入应用时会使用保存的数据。

开始

本教程紧跟前面的教程。如果你想一步步走, 你应该先完成之前的教程。不过, 您可以轻松地将本教程中的概念应用于任何应用程序, 因此如果您不想完成前面的教程也无大碍。

localForage 和 Ionic 存储模块

进入代码之前我们先讨论一点理论知识。我们会在ionic-angular库的Ionic Storage Module代码的基础上进行。具体点说,我们会参考 the Storage class的代码。

Ionic使用 localForage来和存储后台交互。通过使用 localForage我们可以有一些外部的API进行交互,不用管具体使用了哪个技术:Native SQLite storage, IndexedDB, WebSQL或者是简单的浏览器的local storage

Ionic的存储模块封装了localForage ,而且根据可用的选择,去自动的设置合适的方法。这意味着如果你使用Cordova搭建的应用而且安装了SQLite 插件,那这个就会用于存储数据。如果SQLite 不可用它会回头使用IndexedDB 或者 WebSQL,如果这些都不可用那么local storage API就是最后的选择 。

我们想在Ionic/Vue应用里模拟这种行为,所以来看看Ionic是怎么做的。我们会专注于 storage.ts里最关键的部分:

import LocalForage from 'localforage';
import CordovaSQLiteDriver from 'localforage-cordovasqlitedriver';

localForage 库必须安装和导入,如果我们想支持使用SQLite,还需要安装CordovaSQLiteDriver 。使用SQLite 的好处是我们不需要担心浏览器数据被系统清空。

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);
    })
      .then(() => db.setDriver(this._getDriverOrder(actualConfig.driverOrder)))
      .then(() => {
        this._driver = db.driver();
        resolve(db);
      })
      .catch(reason => reject(reason));
  });
}

在构造函数constructor 里创建了一个实例化localForagePromise。我们先要定义CordovaSQLiteDriver,一旦完成我们可以用构造对象创建一个Local Forage实例。

默认的Ionic Storage API 的构造是这样的:

{
  name: '_ionicstorage',
  storeName: '_ionickv',
  driverOrder: ['sqlite', 'indexeddb', 'websql', 'localstorage']
};

这个配置最重要的部分就是drivers,这决定了存储机制的性能。在这个例子里,sqlite是首选,localstorage 是最后的选择。为了确保这些驱动命名正确,用到了下面这个方法:

_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;
    }
  });
}

这个方法会映射driver order数组来使用由localForage提供的合适的值。如果你不熟悉数组的map方法,你可能会对这个视频感兴趣。基本概念就是map操作会对数组里的每个元素进行一些转换。在这个例子里,我们会使用由localForage定义的实际的名称来替换数组里的值。

剩下的API基本都是一些围绕着localForage 的方法的简单封装:

get(key: string): Promise<any> {
  return this._dbPromise.then(db => db.getItem(key));
}

set(key: string, value: any): Promise<any> {
  return this._dbPromise.then(db => db.setItem(key, value));
}

所有Ionic存储模块在做的事情就是通过检查dbPromise 的类成员,来确保存储已经完成了正确的实例化。一旦promise完成,getItemsetItem 方法就会被用来在存储空间里设置(或者销毁)值。

在Vue里创建Storage服务

我们可以直接在我们的组件里使用localForage 接口,但是如果我们想做的像Ionic的Staorage API那样智能,把它独立到它自己的服务里会更有意义。我们将使用刚才学习到的那些概念,用它们在Ionic/Vue应用里来创建一个Storage服务。

首先,我们需要安装必要的依赖库:

npm install localforage --save
npm install localforage-cordovasqlitedriver --save

创建src/services/storage.js文件:

import LocalForage from 'localforage';
import CordovaSQLiteDriver from 'localforage-cordovasqlitedriver';

export default class Storage {

  dbPromise;

  constructor(){

    this.dbPromise = new Promise((resolve, reject) => {

      let db;

      let config = {
          name: '_vuestorage',
          storeName: '_vuekv',
          driverOrder: ['sqlite', 'indexeddb', 'websql', 'localstorage']
      }

      LocalForage.defineDriver(CordovaSQLiteDriver).then(() => {
        db = LocalForage.createInstance(config);
      })
        .then(() => db.setDriver(this.getDriverOrder(config.driverOrder)))
        .then(() => {
          resolve(db);
        })
        .catch(reason => reject(reason));

    });

  }

  ready(){
    return this.dbPromise;
  }

  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;
      }

    });

  }

  get(key){
    return this.dbPromise.then(db => db.getItem(key));
  }

  set(key, value){
    return this.dbPromise.then(db => db.setItem(key, value));
  }

  remove(key){
    return this.dbPromise.then(db => db.removeItem(key));
  }

  clear(){
    return this.dbPromise.then(db => db.clear());
  }

}

这只是Ionic Storage API 相同代码的简化版——它或多或少地以同样的方式起作用。为了看起来更友好我留下了一些功能没封装,但是你没有理由不去实现剩下的那些功能。

使用Storage服务保存和获取数据

现在,我们要做的是使用我们的服务。保存数据很简单:

storage.set('something', 'somevalue');

获取数据:

storage.get('something').then((value) => {
  console.log(value);
});

修改 src/components/HelloWorld.vue如下:

<template>
  <ion-app>
    <ion-header>

        <ion-navbar>
            <ion-title>REDDIT!</ion-title>
        </ion-navbar>

    </ion-header>

    <ion-content>

      <ion-button @click="switchSubreddit('funny')">Funny</ion-button>
      <ion-button @click="switchSubreddit('gifs')">Gifs</ion-button>
      <ion-button @click="switchSubreddit('worldnews')">Worldnews</ion-button>

      <ion-list>
        <ion-item v-for="post in posts" v-bind:key="post.data.id">
          {{post.data.title}}
        </ion-item>
      </ion-list>

    </ion-content>
  </ion-app>
</template>

<script>
import RedditService from '../services/reddit';
import Storage from '../services/storage';

const storage = new Storage();

export default {
  name: 'HelloWorld',
  data () {
    return {
      posts: []
    }
  },
  created() {

    storage.get('subreddit').then((value) => {

      if(value === null){
        value = 'gifs';
      }

      RedditService.getPosts(value).then(response => {
        this.posts = response.body.data.children;
      });

    });

  },
  methods: {
    switchSubreddit(subreddit){

      storage.set('subreddit', subreddit);

      RedditService.getPosts(subreddit).then(response => {
        this.posts = response.body.data.children;
      });     

    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
  font-weight: normal;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

我们引入了刚创建的storage服务,我们首先在created里调用了get方法。created 方法会在组件已创建就执行,所以它会立即检查storagesubreddit存不存在,如果存在,subreddit值会被用于调用API,如果不存在,默认的使用gifs代替。

我们也设置了三个按钮以不同的值触发switchSubreddit 。这个方法会保存新的值到storage,然后调用getPosts。下次应用重启的时候,保存在Storage的值会代替默认的gifs 被使用。

备注:如果你没有看之前的教程,你可能不知道如何使用这个模板的组件。

总结

没有做太多额外的工作,我们就创建了自己的storage服务,和Ionic/Angular应用的存储机制一样的便利和有用。

 类似资料: