1. 程式人生 > >使用Vue構建Ionic混合APP系列教程(四):資料儲存

使用Vue構建Ionic混合APP系列教程(四):資料儲存

大多數應用程式基本都需要儲存一些在應用重新載入時需要的資料。我們經常使用使用者裝置上的本地儲存來實現。當使用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應用的儲存機制一樣的便利和有用。